summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Moser <smoser@ubuntu.com>2016-04-04 12:07:19 -0400
committerScott Moser <smoser@ubuntu.com>2016-04-04 12:07:19 -0400
commit7d8a3194552387fa9e21216bcd9a3bfc76fa2b04 (patch)
treec8dc45b013208a4e5e09e6ade63b3b5994f80aa3
parent93f5af9f5075a416c65c1d0350c374e16f32f0d5 (diff)
parent210b041b2fead7a57af91f60a6f89d9e5aa1ed4a (diff)
downloadvyos-cloud-init-7d8a3194552387fa9e21216bcd9a3bfc76fa2b04.tar.gz
vyos-cloud-init-7d8a3194552387fa9e21216bcd9a3bfc76fa2b04.zip
merge with trunk
-rw-r--r--.bzrignore4
-rw-r--r--ChangeLog78
-rw-r--r--MANIFEST.in8
-rw-r--r--Makefile46
-rwxr-xr-xbin/cloud-init139
-rw-r--r--cloudinit/cloud.py9
-rw-r--r--cloudinit/config/cc_apt_configure.py14
-rw-r--r--cloudinit/config/cc_apt_pipelining.py2
-rw-r--r--cloudinit/config/cc_bootcmd.py2
-rw-r--r--cloudinit/config/cc_ca_certs.py4
-rw-r--r--cloudinit/config/cc_chef.py6
-rw-r--r--cloudinit/config/cc_debug.py7
-rw-r--r--cloudinit/config/cc_disk_setup.py165
-rw-r--r--cloudinit/config/cc_emit_upstart.py27
-rw-r--r--cloudinit/config/cc_fan.py101
-rw-r--r--cloudinit/config/cc_final_message.py9
-rw-r--r--cloudinit/config/cc_growpart.py2
-rw-r--r--cloudinit/config/cc_grub_dpkg.py26
-rw-r--r--cloudinit/config/cc_keys_to_console.py2
-rw-r--r--cloudinit/config/cc_landscape.py14
-rw-r--r--cloudinit/config/cc_locale.py6
-rw-r--r--cloudinit/config/cc_lxd.py85
-rw-r--r--cloudinit/config/cc_mcollective.py15
-rw-r--r--cloudinit/config/cc_mounts.py123
-rw-r--r--cloudinit/config/cc_phone_home.py4
-rw-r--r--cloudinit/config/cc_power_state_change.py57
-rw-r--r--cloudinit/config/cc_puppet.py16
-rw-r--r--cloudinit/config/cc_resizefs.py2
-rw-r--r--cloudinit/config/cc_resolv_conf.py4
-rw-r--r--cloudinit/config/cc_rh_subscription.py402
-rw-r--r--cloudinit/config/cc_rightscale_userdata.py6
-rw-r--r--cloudinit/config/cc_rsyslog.py343
-rw-r--r--cloudinit/config/cc_runcmd.py2
-rw-r--r--cloudinit/config/cc_salt_minion.py2
-rw-r--r--cloudinit/config/cc_seed_random.py17
-rw-r--r--cloudinit/config/cc_set_hostname.py2
-rw-r--r--cloudinit/config/cc_set_passwords.py25
-rw-r--r--cloudinit/config/cc_snappy.py304
-rw-r--r--cloudinit/config/cc_ssh.py74
-rw-r--r--cloudinit/config/cc_ssh_authkey_fingerprints.py2
-rw-r--r--cloudinit/config/cc_update_etc_hosts.py6
-rw-r--r--cloudinit/config/cc_update_hostname.py2
-rw-r--r--cloudinit/config/cc_write_files.py5
-rw-r--r--cloudinit/config/cc_yum_add_repo.py9
-rw-r--r--cloudinit/cs_utils.py4
-rw-r--r--cloudinit/distros/__init__.py173
-rw-r--r--cloudinit/distros/arch.py10
-rw-r--r--cloudinit/distros/debian.py20
-rw-r--r--cloudinit/distros/freebsd.py13
-rw-r--r--cloudinit/distros/gentoo.py6
-rw-r--r--cloudinit/distros/net_util.py2
-rw-r--r--cloudinit/distros/parsers/hostname.py4
-rw-r--r--cloudinit/distros/parsers/hosts.py2
-rw-r--r--cloudinit/distros/parsers/resolv_conf.py4
-rw-r--r--cloudinit/distros/parsers/sys_conf.py12
-rw-r--r--cloudinit/distros/rhel.py19
-rw-r--r--cloudinit/distros/rhel_util.py4
-rw-r--r--cloudinit/distros/sles.py4
-rw-r--r--cloudinit/ec2_utils.py22
-rw-r--r--cloudinit/filters/launch_index.py2
-rw-r--r--cloudinit/handlers/__init__.py43
-rw-r--r--cloudinit/handlers/boot_hook.py2
-rw-r--r--cloudinit/handlers/cloud_config.py2
-rw-r--r--cloudinit/handlers/shell_script.py2
-rw-r--r--cloudinit/handlers/upstart_job.py2
-rw-r--r--cloudinit/helpers.py45
-rw-r--r--cloudinit/log.py7
-rw-r--r--cloudinit/mergers/__init__.py4
-rw-r--r--cloudinit/mergers/m_dict.py4
-rw-r--r--cloudinit/mergers/m_list.py6
-rw-r--r--cloudinit/mergers/m_str.py10
-rw-r--r--cloudinit/net/__init__.py751
-rw-r--r--cloudinit/net/network_state.py446
-rw-r--r--cloudinit/net/udev.py54
-rw-r--r--cloudinit/netinfo.py4
-rw-r--r--cloudinit/registry.py37
-rw-r--r--cloudinit/reporting/__init__.py42
-rw-r--r--cloudinit/reporting/events.py246
-rw-r--r--cloudinit/reporting/handlers.py91
-rw-r--r--cloudinit/settings.py3
-rw-r--r--cloudinit/signal_handler.py2
-rw-r--r--cloudinit/sources/DataSourceAltCloud.py39
-rw-r--r--cloudinit/sources/DataSourceAzure.py324
-rw-r--r--cloudinit/sources/DataSourceBigstep.py57
-rw-r--r--cloudinit/sources/DataSourceCloudSigma.py22
-rw-r--r--cloudinit/sources/DataSourceCloudStack.py80
-rw-r--r--cloudinit/sources/DataSourceConfigDrive.py145
-rw-r--r--cloudinit/sources/DataSourceDigitalOcean.py23
-rw-r--r--cloudinit/sources/DataSourceEc2.py23
-rw-r--r--cloudinit/sources/DataSourceGCE.py103
-rw-r--r--cloudinit/sources/DataSourceMAAS.py213
-rw-r--r--cloudinit/sources/DataSourceNoCloud.py131
-rw-r--r--cloudinit/sources/DataSourceNone.py4
-rw-r--r--cloudinit/sources/DataSourceOVF.py141
-rw-r--r--cloudinit/sources/DataSourceOpenNebula.py20
-rw-r--r--cloudinit/sources/DataSourceOpenStack.py4
-rw-r--r--cloudinit/sources/DataSourceSmartOS.py337
-rw-r--r--cloudinit/sources/__init__.py62
-rw-r--r--cloudinit/sources/helpers/azure.py278
-rw-r--r--cloudinit/sources/helpers/openstack.py21
-rw-r--r--cloudinit/sources/helpers/vmware/__init__.py13
-rw-r--r--cloudinit/sources/helpers/vmware/imc/__init__.py13
-rw-r--r--cloudinit/sources/helpers/vmware/imc/boot_proto.py25
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config.py95
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config_file.py129
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config_namespace.py25
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config_nic.py247
-rw-r--r--cloudinit/sources/helpers/vmware/imc/config_source.py23
-rw-r--r--cloudinit/sources/helpers/vmware/imc/guestcust_error.py24
-rw-r--r--cloudinit/sources/helpers/vmware/imc/guestcust_event.py27
-rw-r--r--cloudinit/sources/helpers/vmware/imc/guestcust_state.py25
-rw-r--r--cloudinit/sources/helpers/vmware/imc/guestcust_util.py128
-rw-r--r--cloudinit/sources/helpers/vmware/imc/ipv4_mode.py45
-rw-r--r--cloudinit/sources/helpers/vmware/imc/nic.py147
-rw-r--r--cloudinit/sources/helpers/vmware/imc/nic_base.py154
-rw-r--r--cloudinit/ssh_util.py9
-rw-r--r--cloudinit/stages.py241
-rw-r--r--cloudinit/templater.py2
-rw-r--r--cloudinit/type_utils.py32
-rw-r--r--cloudinit/url_helper.py196
-rw-r--r--cloudinit/user_data.py27
-rw-r--r--cloudinit/util.py355
-rw-r--r--config/cloud.cfg6
-rw-r--r--doc/examples/cloud-config-datasources.txt7
-rw-r--r--doc/examples/cloud-config-lxd.txt28
-rw-r--r--doc/examples/cloud-config-mount-points.txt2
-rw-r--r--doc/examples/cloud-config-power-state.txt9
-rw-r--r--doc/examples/cloud-config-reporting.txt17
-rw-r--r--doc/examples/cloud-config-rh_subscription.txt49
-rw-r--r--doc/examples/cloud-config-rsyslog.txt46
-rw-r--r--doc/examples/cloud-config-seed-random.txt32
-rw-r--r--doc/examples/cloud-config-user-groups.txt6
-rw-r--r--doc/examples/cloud-config.txt6
-rw-r--r--doc/rtd/conf.py4
-rw-r--r--doc/rtd/static/logo.pngbin16031 -> 12751 bytes
-rw-r--r--doc/rtd/static/logo.svg14445
-rw-r--r--doc/rtd/topics/datasources.rst8
-rw-r--r--doc/sources/cloudstack/README.rst29
-rw-r--r--doc/sources/nocloud/README.rst2
-rw-r--r--doc/sources/openstack/README.rst24
-rwxr-xr-xpackages/bddeb110
-rwxr-xr-xpackages/brpm6
-rw-r--r--packages/debian/changelog.in2
-rw-r--r--packages/debian/cloud-init.postinst16
-rw-r--r--packages/debian/cloud-init.preinst20
-rw-r--r--packages/debian/control.in33
-rw-r--r--packages/debian/copyright2
-rw-r--r--packages/debian/dirs1
-rwxr-xr-xpackages/debian/rules17
-rwxr-xr-xpackages/debian/rules.in23
-rw-r--r--requirements.txt6
-rwxr-xr-xsetup.py86
-rw-r--r--systemd/cloud-config.service7
-rw-r--r--systemd/cloud-config.target3
-rw-r--r--systemd/cloud-final.service8
-rwxr-xr-xsystemd/cloud-init-generator133
-rw-r--r--systemd/cloud-init-local.service8
-rw-r--r--systemd/cloud-init.service8
-rw-r--r--systemd/cloud-init.target6
-rwxr-xr-xsysvinit/redhat/cloud-init-local5
-rw-r--r--templates/resolv.conf.tmpl2
-rw-r--r--templates/sources.list.ubuntu.tmpl38
-rw-r--r--test-requirements.txt3
-rw-r--r--tests/data/vmware/cust-dhcp-2nic.cfg34
-rw-r--r--tests/data/vmware/cust-static-2nic.cfg39
-rw-r--r--tests/unittests/helpers.py189
-rw-r--r--tests/unittests/test__init__.py236
-rw-r--r--tests/unittests/test_builtin_handlers.py39
-rw-r--r--tests/unittests/test_cli.py54
-rw-r--r--tests/unittests/test_cs_util.py39
-rw-r--r--tests/unittests/test_data.py241
-rw-r--r--tests/unittests/test_datasource/test_altcloud.py95
-rw-r--r--tests/unittests/test_datasource/test_azure.py526
-rw-r--r--tests/unittests/test_datasource/test_azure_helper.py420
-rw-r--r--tests/unittests/test_datasource/test_cloudsigma.py1
-rw-r--r--tests/unittests/test_datasource/test_cloudstack.py86
-rw-r--r--tests/unittests/test_datasource/test_configdrive.py207
-rw-r--r--tests/unittests/test_datasource/test_digitalocean.py10
-rw-r--r--tests/unittests/test_datasource/test_gce.py86
-rw-r--r--tests/unittests/test_datasource/test_maas.py92
-rw-r--r--tests/unittests/test_datasource/test_nocloud.py73
-rw-r--r--tests/unittests/test_datasource/test_opennebula.py25
-rw-r--r--tests/unittests/test_datasource/test_openstack.py15
-rw-r--r--tests/unittests/test_datasource/test_smartos.py284
-rw-r--r--tests/unittests/test_distros/test_generic.py51
-rw-r--r--tests/unittests/test_distros/test_hostname.py4
-rw-r--r--tests/unittests/test_distros/test_hosts.py4
-rw-r--r--tests/unittests/test_distros/test_netconfig.py282
-rw-r--r--tests/unittests/test_distros/test_resolv.py5
-rw-r--r--tests/unittests/test_distros/test_sysconfig.py5
-rw-r--r--tests/unittests/test_distros/test_user_data_normalize.py36
-rw-r--r--tests/unittests/test_ec2_util.py4
-rw-r--r--tests/unittests/test_filters/test_launch_index.py8
-rw-r--r--tests/unittests/test_handler/test_handler_apt_configure.py35
-rw-r--r--tests/unittests/test_handler/test_handler_ca_certs.py254
-rw-r--r--tests/unittests/test_handler/test_handler_chef.py18
-rw-r--r--tests/unittests/test_handler/test_handler_debug.py5
-rw-r--r--tests/unittests/test_handler/test_handler_disk_setup.py30
-rw-r--r--tests/unittests/test_handler/test_handler_growpart.py110
-rw-r--r--tests/unittests/test_handler/test_handler_locale.py11
-rw-r--r--tests/unittests/test_handler/test_handler_lxd.py75
-rw-r--r--tests/unittests/test_handler/test_handler_mounts.py133
-rw-r--r--tests/unittests/test_handler/test_handler_power_state.py47
-rw-r--r--tests/unittests/test_handler/test_handler_rsyslog.py174
-rw-r--r--tests/unittests/test_handler/test_handler_seed_random.py28
-rw-r--r--tests/unittests/test_handler/test_handler_set_hostname.py11
-rw-r--r--tests/unittests/test_handler/test_handler_snappy.py305
-rw-r--r--tests/unittests/test_handler/test_handler_timezone.py11
-rw-r--r--tests/unittests/test_handler/test_handler_yum_add_repo.py12
-rw-r--r--tests/unittests/test_merging.py16
-rw-r--r--tests/unittests/test_net.py127
-rw-r--r--tests/unittests/test_pathprefix2dict.py22
-rw-r--r--tests/unittests/test_registry.py28
-rw-r--r--tests/unittests/test_reporting.py369
-rw-r--r--tests/unittests/test_rh_subscription.py208
-rw-r--r--tests/unittests/test_runs/test_merge_run.py8
-rw-r--r--tests/unittests/test_runs/test_simple_run.py11
-rw-r--r--tests/unittests/test_sshutil.py3
-rw-r--r--tests/unittests/test_templating.py13
-rw-r--r--tests/unittests/test_util.py234
-rw-r--r--tests/unittests/test_vmware_config_file.py103
-rwxr-xr-xtools/ccfg-merge-debug4
-rwxr-xr-xtools/hacking.py18
-rwxr-xr-xtools/mock-meta.py27
-rwxr-xr-xtools/read-dependencies10
-rwxr-xr-xtools/run-pep851
-rwxr-xr-xtools/run-pyflakes18
-rwxr-xr-xtools/run-pyflakes32
-rwxr-xr-xtools/tox-venv42
-rwxr-xr-xtools/validate-yaml.py5
-rw-r--r--tox.ini32
-rw-r--r--udev/66-azure-ephemeral.rules18
-rw-r--r--udev/79-cloud-init-net-wait.rules10
-rwxr-xr-xudev/cloud-init-wait70
234 files changed, 12355 insertions, 17097 deletions
diff --git a/.bzrignore b/.bzrignore
new file mode 100644
index 00000000..926e4581
--- /dev/null
+++ b/.bzrignore
@@ -0,0 +1,4 @@
+.tox
+dist
+cloud_init.egg-info
+__pycache__
diff --git a/ChangeLog b/ChangeLog
index ddde383a..6d6da417 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -20,6 +20,83 @@
- systemd: make init stage run before login prompts shown [Steve Langasek]
- hostname: on first boot apply hostname to be same as is written for
persistent hostname. (LP: #1246485)
+ - remove usage of dmidecode on linux in favor of /sys interface [Ben Howard]
+ - python3 support [Barry Warsaw, Daniel Watkins, Josh Harlow] (LP: #1247132)
+ - support managing gpt partitions in disk config [Daniel Watkins]
+ - Azure: utilze gpt support for ephemeral formating [Daniel Watkins]
+ - CloudStack: support fetching password from virtual router [Daniel Watkins]
+ (LP: #1422388)
+ - readurl, read_file_or_url returns bytes, user must convert as necessary
+ - SmartOS: use v2 metadata service (LP: #1436417) [Daniel Watkins]
+ - NoCloud: fix local datasource claiming found without explicit dsmode
+ - Snappy: add support for installing snappy packages and configuring.
+ - systemd: use network-online instead of network.target (LP: #1440180)
+ [Steve Langasek]
+ - Add functionality to fixate the uid of a newly added user.
+ - Don't overwrite the hostname if the user has changed it after we set it.
+ - GCE datasource does not handle instance ssh keys (LP: 1403617)
+ - sysvinit: make cloud-init-local run before network (LP: #1275098)
+ [Surojit Pathak]
+ - Azure: do not re-set hostname if user has changed it (LP: #1375252)
+ - Fix exception when running with no arguments on Python 3. [Daniel Watkins]
+ - Centos: detect/expect use of systemd on centos 7. [Brian Rak]
+ - Azure: remove dependency on walinux-agent [Daniel Watkins]
+ - EC2: know about eu-central-1 availability-zone (LP: #1456684)
+ - Azure: remove password from on-disk ovf-env.xml (LP: #1443311) [Ben Howard]
+ - Doc: include information on user-data in OpenStack [Daniel Watkins]
+ - Systemd: check for systemd using sd_booted symantics (LP: #1461201)
+ [Lars Kellogg-Stedman]
+ - Add an rh_subscription module to handle registration of Red Hat instances.
+ [Brent Baude]
+ - cc_apt_configure: fix importing keys under python3 (LP: #1463373)
+ - cc_growpart: fix specification of 'devices' list (LP: #1465436)
+ - CloudStack: fix password setting on cloudstack > 4.5.1 (LP: #1464253)
+ - GCE: fix determination of availability zone (LP: #1470880)
+ - ssh: generate ed25519 host keys (LP: #1461242)
+ - distro mirrors: provide datasource to mirror selection code to support
+ GCE regional mirrors. (LP: #1470890)
+ - add udev rules that identify ephemeral device on Azure (LP: #1411582)
+ - _read_dmi_syspath: fix bad log message causing unintended exception
+ - rsyslog: add additional configuration mode (LP: #1478103)
+ - status_wrapper in main: fix use of print_exc when handling exception
+ - reporting: add reporting module for web hook or logging of events.
+ - NoCloud: fix consumption of vendordata (LP: #1493453)
+ - power_state_change: support 'condition' to disable or enable poweroff
+ - ubuntu fan: support for config and installing of ubuntu fan (LP: #1504604)
+ - Azure: support extracting SSH key values from ovf-env.xml (LP: #1506244)
+ - AltCloud: fix call to udevadm settle (LP: #1507526)
+ - Ubuntu templates: modify sources.list template to provide same sources
+ as install from server or desktop ISO. (LP: #1177432)
+ - cc_mounts: use 'nofail' if system uses systemd. (LP: #1514485)
+ - Azure: get instance id from dmi instead of SharedConfig (LP: #1506187)
+ - systemd/power_state: fix power_state to work even if cloud-final
+ exited non-zero (LP: #1449318)
+ - SmartOS: Add support for Joyent LX-Brand Zones (LP: #1540965)
+ [Robert C Jennings]
+ - systemd: support using systemd-detect-virt to detect container
+ (LP: #1539016) [Martin Pitt]
+ - docs: fix lock_passwd documentation [Robert C Jennings]
+ - Azure: Handle escaped quotes in WALinuxAgentShim.find_endpoint.
+ (LP: #1488891) [Dan Watkins]
+ - lxd: add support for setting up lxd using 'lxd init' (LP: #1522879)
+ - Add Image Customization Parser for VMware vSphere Hypervisor
+ Support. [Sankar Tanguturi]
+ - timezone: use a symlink rather than copy for /etc/localtime
+ unless it is already a file (LP: #1543025).
+ - Enable password changing via a hashed string [Alex Sirbu]
+ - Added BigStep datasource [Alex Sirbu]
+ - No longer run pollinate in seed_random (LP: #1554152)
+ - groups: add defalt user to 'lxd' group. Create groups listed
+ for a user if they do not exist. (LP: #1539317)
+ - dmi data: fix failure of reading dmi data for unset dmi values
+ - doc: mention label for nocloud datasource must be 'cidata' [Peter Hurley]
+ - ssh_pwauth: fix module to support 'unchanged' and match behavior
+ described in documentation [Chris Cosby]
+ - quickly check to see if the previous instance id is still valid to
+ avoid dependency on network metadata service on every boot (LP: #1553815)
+ - support network configuration in cloud-init --local with support
+ device naming via systemd.link.
+
0.7.6:
- open 0.7.6
- Enable vendordata on CloudSigma datasource (LP: #1303986)
@@ -61,6 +138,7 @@
(LP: #1336855)
- FreeBsd: support config drive datasource [Joseph bajin]
- cc_mounts: support creating a swap file
+ - DigitalOcean & GCE: fix get_hostname consistency
0.7.5:
- open 0.7.5
- Add a debug log message around import failures
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 00000000..90f6c7d5
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,8 @@
+include *.py MANIFEST.in ChangeLog
+global-include *.txt *.rst *.ini *.in *.conf *.cfg *.sh
+graft tools
+prune build
+prune dist
+prune .tox
+prune .bzr
+exclude .bzrignore
diff --git a/Makefile b/Makefile
index 009257ca..32c50aee 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
CWD=$(shell pwd)
-PY_FILES=$(shell find cloudinit bin tests tools -name "*.py" -type f )
-PY_FILES+="bin/cloud-init"
+PYVER ?= 3
+noseopts ?= -v
YAML_FILES=$(shell find cloudinit bin tests tools -name "*.yaml" -type f )
YAML_FILES+=$(shell find doc/examples -name "cloud-config*.txt" -type f )
@@ -10,17 +10,42 @@ CODE_VERSION=$(shell python -c "from cloudinit import version; print version.ver
PIP_INSTALL := pip install
+ifeq ($(PYVER),3)
+ pyflakes = pyflakes3
+ unittests = unittest3
+ yaml = yaml
+else
+ifeq ($(PYVER),2)
+ pyflakes = pyflakes
+ unittests = unittest
+else
+ pyflakes = pyflakes pyflakes3
+ unittests = unittest unittest3
+endif
+endif
+
ifeq ($(distro),)
distro = redhat
endif
-all: test check_version
+all: check
+
+check: check_version pep8 $(pyflakes) test $(yaml)
pep8:
- @$(CWD)/tools/run-pep8 $(PY_FILES)
+ @$(CWD)/tools/run-pep8
pyflakes:
- pyflakes $(PY_FILES)
+ @$(CWD)/tools/run-pyflakes
+
+pyflakes3:
+ @$(CWD)/tools/run-pyflakes3
+
+unittest: clean_pyc
+ nosetests $(noseopts) tests/unittests
+
+unittest3: clean_pyc
+ nosetests3 $(noseopts) tests/unittests
pip-requirements:
@echo "Installing cloud-init dependencies..."
@@ -30,9 +55,7 @@ pip-test-requirements:
@echo "Installing cloud-init test dependencies..."
$(PIP_INSTALL) -r "$@.txt" -q
-test: clean_pyc
- @echo "Running tests..."
- @nosetests $(noseopts) tests/
+test: $(unittests)
check_version:
@if [ "$(CHANGELOG_VERSION)" != "$(CODE_VERSION)" ]; then \
@@ -43,9 +66,6 @@ check_version:
clean_pyc:
@find . -type f -name "*.pyc" -delete
-2to3:
- 2to3 $(PY_FILES)
-
clean: clean_pyc
rm -rf /var/log/cloud-init.log /var/lib/cloud/
@@ -58,5 +78,5 @@ rpm:
deb:
./packages/bddeb
-.PHONY: test pyflakes 2to3 clean pep8 rpm deb yaml check_version
-.PHONY: pip-test-requirements pip-requirements clean_pyc
+.PHONY: test pyflakes pyflakes3 clean pep8 rpm deb yaml check_version
+.PHONY: pip-test-requirements pip-requirements clean_pyc unittest unittest3
diff --git a/bin/cloud-init b/bin/cloud-init
index 866f8ca4..715be4b5 100755
--- a/bin/cloud-init
+++ b/bin/cloud-init
@@ -46,6 +46,8 @@ from cloudinit import sources
from cloudinit import stages
from cloudinit import templater
from cloudinit import util
+from cloudinit import reporting
+from cloudinit.reporting import events
from cloudinit import version
from cloudinit.settings import (PER_INSTANCE, PER_ALWAYS, PER_ONCE,
@@ -136,6 +138,11 @@ def run_module_section(mods, action_name, section):
return failures
+def apply_reporting_cfg(cfg):
+ if cfg.get('reporting'):
+ reporting.update_configuration(cfg.get('reporting'))
+
+
def main_init(name, args):
deps = [sources.DEP_FILESYSTEM, sources.DEP_NETWORK]
if args.local:
@@ -171,7 +178,7 @@ def main_init(name, args):
w_msg = welcome_format(name)
else:
w_msg = welcome_format("%s-local" % (name))
- init = stages.Init(deps)
+ init = stages.Init(ds_deps=deps, reporter=args.reporter)
# Stage 1
init.read_cfg(extract_fns(args))
# Stage 2
@@ -187,9 +194,10 @@ def main_init(name, args):
if args.debug:
# Reset so that all the debug handlers are closed out
LOG.debug(("Logging being reset, this logger may no"
- " longer be active shortly"))
+ " longer be active shortly"))
logging.resetLogging()
logging.setupLogging(init.cfg)
+ apply_reporting_cfg(init.cfg)
# Any log usage prior to setupLogging above did not have local user log
# config applied. We send the welcome message now, as stderr/out have
@@ -204,6 +212,7 @@ def main_init(name, args):
# Stage 4
path_helper = init.paths
if not args.local:
+ existing = "trust"
sys.stderr.write("%s\n" % (netinfo.debug_info()))
LOG.debug(("Checking to see if files that we need already"
" exist from a previous run that would allow us"
@@ -228,21 +237,17 @@ def main_init(name, args):
LOG.debug("Execution continuing, no previous run detected that"
" would allow us to stop early.")
else:
- # The cache is not instance specific, so it has to be purged
- # but we want 'start' to benefit from a cache if
- # a previous start-local populated one...
- manual_clean = util.get_cfg_option_bool(init.cfg,
- 'manual_cache_clean', False)
- if manual_clean:
- LOG.debug("Not purging instance link, manual cleaning enabled")
- init.purge_cache(False)
- else:
- init.purge_cache()
+ existing = "check"
+ if util.get_cfg_option_bool(init.cfg, 'manual_cache_clean', False):
+ existing = "trust"
+
+ init.purge_cache()
# Delete the non-net file as well
util.del_file(os.path.join(path_helper.get_cpath("data"), "no-net"))
+
# Stage 5
try:
- init.fetch()
+ init.fetch(existing=existing)
except sources.DataSourceNotFoundException:
# In the case of 'cloud-init init' without '--local' it is a bit
# more likely that the user would consider it failure if nothing was
@@ -254,10 +259,15 @@ def main_init(name, args):
util.logexc(LOG, ("No instance datasource found!"
" Likely bad things to come!"))
if not args.force:
+ init.apply_network_config()
if args.local:
return (None, [])
else:
return (None, ["No instance datasource found."])
+
+ if args.local:
+ init.apply_network_config()
+
# Stage 6
iid = init.instancify()
LOG.debug("%s will now be targeting instance id: %s", name, iid)
@@ -268,9 +278,9 @@ def main_init(name, args):
# This may run user-data handlers and/or perform
# url downloads and such as needed.
(ran, _results) = init.cloudify().run('consume_data',
- init.consume_data,
- args=[PER_INSTANCE],
- freq=PER_INSTANCE)
+ init.consume_data,
+ args=[PER_INSTANCE],
+ freq=PER_INSTANCE)
if not ran:
# Just consume anything that is set to run per-always
# if nothing ran in the per-instance code
@@ -282,8 +292,10 @@ def main_init(name, args):
util.logexc(LOG, "Consuming user data failed!")
return (init.datasource, ["Consuming user data failed!"])
+ apply_reporting_cfg(init.cfg)
+
# Stage 8 - re-read and apply relevant cloud-config to include user-data
- mods = stages.Modules(init, extract_fns(args))
+ mods = stages.Modules(init, extract_fns(args), reporter=args.reporter)
# Stage 9
try:
outfmt_orig = outfmt
@@ -313,12 +325,12 @@ def main_modules(action_name, args):
# 5. Run the modules for the given stage name
# 6. Done!
w_msg = welcome_format("%s:%s" % (action_name, name))
- init = stages.Init(ds_deps=[])
+ init = stages.Init(ds_deps=[], reporter=args.reporter)
# Stage 1
init.read_cfg(extract_fns(args))
# Stage 2
try:
- init.fetch()
+ init.fetch(existing="trust")
except sources.DataSourceNotFoundException:
# There was no datasource found, theres nothing to do
msg = ('Can not apply stage %s, no datasource found! Likely bad '
@@ -328,7 +340,7 @@ def main_modules(action_name, args):
if not args.force:
return [(msg)]
# Stage 3
- mods = stages.Modules(init, extract_fns(args))
+ mods = stages.Modules(init, extract_fns(args), reporter=args.reporter)
# Stage 4
try:
LOG.debug("Closing stdin")
@@ -339,9 +351,10 @@ def main_modules(action_name, args):
if args.debug:
# Reset so that all the debug handlers are closed out
LOG.debug(("Logging being reset, this logger may no"
- " longer be active shortly"))
+ " longer be active shortly"))
logging.resetLogging()
logging.setupLogging(mods.cfg)
+ apply_reporting_cfg(init.cfg)
# now that logging is setup and stdout redirected, send welcome
welcome(name, msg=w_msg)
@@ -366,12 +379,12 @@ def main_single(name, args):
# 6. Done!
mod_name = args.name
w_msg = welcome_format(name)
- init = stages.Init(ds_deps=[])
+ init = stages.Init(ds_deps=[], reporter=args.reporter)
# Stage 1
init.read_cfg(extract_fns(args))
# Stage 2
try:
- init.fetch()
+ init.fetch(existing="trust")
except sources.DataSourceNotFoundException:
# There was no datasource found,
# that might be bad (or ok) depending on
@@ -383,7 +396,7 @@ def main_single(name, args):
if not args.force:
return 1
# Stage 3
- mods = stages.Modules(init, extract_fns(args))
+ mods = stages.Modules(init, extract_fns(args), reporter=args.reporter)
mod_args = args.module_args
if mod_args:
LOG.debug("Using passed in arguments %s", mod_args)
@@ -404,6 +417,7 @@ def main_single(name, args):
" longer be active shortly"))
logging.resetLogging()
logging.setupLogging(mods.cfg)
+ apply_reporting_cfg(init.cfg)
# now that logging is setup and stdout redirected, send welcome
welcome(name, msg=w_msg)
@@ -423,20 +437,24 @@ def main_single(name, args):
return 0
-def atomic_write_json(path, data):
+def atomic_write_file(path, content, mode='w'):
tf = None
try:
tf = tempfile.NamedTemporaryFile(dir=os.path.dirname(path),
- delete=False)
- tf.write(json.dumps(data, indent=1) + "\n")
+ delete=False, mode=mode)
+ tf.write(content)
tf.close()
os.rename(tf.name, path)
except Exception as e:
if tf is not None:
- util.del_file(tf.name)
+ os.unlink(tf.name)
raise e
+def atomic_write_json(path, data):
+ return atomic_write_file(path, json.dumps(data, indent=1) + "\n")
+
+
def status_wrapper(name, args, data_d=None, link_d=None):
if data_d is None:
data_d = os.path.normpath("/var/lib/cloud/data")
@@ -505,6 +523,8 @@ def status_wrapper(name, args, data_d=None, link_d=None):
v1[mode]['errors'] = [str(e) for e in errors]
except Exception as e:
+ util.logexc(LOG, "failed of stage %s", mode)
+ print_exc("failed run of stage %s" % mode)
v1[mode]['errors'] = [str(e)]
v1[mode]['finished'] = time.time()
@@ -520,7 +540,8 @@ def status_wrapper(name, args, data_d=None, link_d=None):
errors.extend(v1[m].get('errors', []))
atomic_write_json(result_path,
- {'v1': {'datasource': v1['datasource'], 'errors': errors}})
+ {'v1': {'datasource': v1['datasource'],
+ 'errors': errors}})
util.sym_link(os.path.relpath(result_path, link_d), result_link,
force=True)
@@ -547,6 +568,8 @@ def main():
' found (use at your own risk)'),
dest='force',
default=False)
+
+ parser.set_defaults(reporter=None)
subparsers = parser.add_subparsers()
# Each action and its sub-options (if any)
@@ -562,13 +585,13 @@ def main():
# These settings are used for the 'config' and 'final' stages
parser_mod = subparsers.add_parser('modules',
- help=('activates modules '
- 'using a given configuration key'))
+ help=('activates modules using '
+ 'a given configuration key'))
parser_mod.add_argument("--mode", '-m', action='store',
- help=("module configuration name "
- "to use (default: %(default)s)"),
- default='config',
- choices=('init', 'config', 'final'))
+ help=("module configuration name "
+ "to use (default: %(default)s)"),
+ default='config',
+ choices=('init', 'config', 'final'))
parser_mod.set_defaults(action=('modules', main_modules))
# These settings are used when you want to query information
@@ -584,19 +607,22 @@ def main():
# This subcommand allows you to run a single module
parser_single = subparsers.add_parser('single',
- help=('run a single module '))
+ help=('run a single module '))
parser_single.set_defaults(action=('single', main_single))
parser_single.add_argument("--name", '-n', action="store",
- help="module name to run",
- required=True)
+ help="module name to run",
+ required=True)
parser_single.add_argument("--frequency", action="store",
- help=("frequency of the module"),
- required=False,
- choices=list(FREQ_SHORT_NAMES.keys()))
+ help=("frequency of the module"),
+ required=False,
+ choices=list(FREQ_SHORT_NAMES.keys()))
+ parser_single.add_argument("--report", action="store_true",
+ help="enable reporting",
+ required=False)
parser_single.add_argument("module_args", nargs="*",
- metavar='argument',
- help=('any additional arguments to'
- ' pass to this module'))
+ metavar='argument',
+ help=('any additional arguments to'
+ ' pass to this module'))
parser_single.set_defaults(action=('single', main_single))
args = parser.parse_args()
@@ -609,12 +635,33 @@ def main():
# Setup signal handlers before running
signal_handler.attach_handlers()
+ if not hasattr(args, 'action'):
+ parser.error('too few arguments')
(name, functor) = args.action
if name in ("modules", "init"):
functor = status_wrapper
- return util.log_time(logfunc=LOG.debug, msg="cloud-init mode '%s'" % name,
- get_uptime=True, func=functor, args=(name, args))
+ report_on = True
+ if name == "init":
+ if args.local:
+ rname, rdesc = ("init-local", "searching for local datasources")
+ else:
+ rname, rdesc = ("init-network",
+ "searching for network datasources")
+ elif name == "modules":
+ rname, rdesc = ("modules-%s" % args.mode,
+ "running modules for %s" % args.mode)
+ elif name == "single":
+ rname, rdesc = ("single/%s" % args.name,
+ "running single module %s" % args.name)
+ report_on = args.report
+
+ args.reporter = events.ReportEventStack(
+ rname, rdesc, reporting_enabled=report_on)
+ with args.reporter:
+ return util.log_time(
+ logfunc=LOG.debug, msg="cloud-init mode '%s'" % name,
+ get_uptime=True, func=functor, args=(name, args))
if __name__ == '__main__':
diff --git a/cloudinit/cloud.py b/cloudinit/cloud.py
index 95e0cfb2..3e6be203 100644
--- a/cloudinit/cloud.py
+++ b/cloudinit/cloud.py
@@ -24,6 +24,7 @@ import copy
import os
from cloudinit import log as logging
+from cloudinit.reporting import events
LOG = logging.getLogger(__name__)
@@ -40,12 +41,18 @@ LOG = logging.getLogger(__name__)
class Cloud(object):
- def __init__(self, datasource, paths, cfg, distro, runners):
+ def __init__(self, datasource, paths, cfg, distro, runners, reporter=None):
self.datasource = datasource
self.paths = paths
self.distro = distro
self._cfg = cfg
self._runners = runners
+ if reporter is None:
+ reporter = events.ReportEventStack(
+ name="unnamed-cloud-reporter",
+ description="unnamed-cloud-reporter",
+ reporting_enabled=False)
+ self.reporter = reporter
# If a 'user' manipulates logging or logging services
# it is typically useful to cause the logging to be
diff --git a/cloudinit/config/cc_apt_configure.py b/cloudinit/config/cc_apt_configure.py
index f10b76a3..702977cb 100644
--- a/cloudinit/config/cc_apt_configure.py
+++ b/cloudinit/config/cc_apt_configure.py
@@ -51,6 +51,10 @@ EXPORT_GPG_KEYID = """
def handle(name, cfg, cloud, log, _args):
+ if util.is_false(cfg.get('apt_configure_enabled', True)):
+ log.debug("Skipping module named %s, disabled by config.", name)
+ return
+
release = get_release()
mirrors = find_apt_mirror_info(cloud, cfg)
if not mirrors or "primary" not in mirrors:
@@ -87,7 +91,8 @@ def handle(name, cfg, cloud, log, _args):
if matchcfg:
matcher = re.compile(matchcfg).search
else:
- matcher = lambda f: False
+ def matcher(x):
+ return False
errors = add_sources(cfg['apt_sources'], params,
aa_repo_match=matcher)
@@ -105,7 +110,7 @@ def handle(name, cfg, cloud, log, _args):
# get gpg keyid from keyserver
def getkeybyid(keyid, keyserver):
- with util.ExtendedTemporaryFile(suffix='.sh') as fh:
+ with util.ExtendedTemporaryFile(suffix='.sh', mode="w+", ) as fh:
fh.write(EXPORT_GPG_KEYID)
fh.flush()
cmd = ['/bin/sh', fh.name, keyid, keyserver]
@@ -126,7 +131,7 @@ def mirror2lists_fileprefix(mirror):
def rename_apt_lists(old_mirrors, new_mirrors, lists_d="/var/lib/apt/lists"):
- for (name, omirror) in old_mirrors.iteritems():
+ for (name, omirror) in old_mirrors.items():
nmirror = new_mirrors.get(name)
if not nmirror:
continue
@@ -169,7 +174,8 @@ def add_sources(srclist, template_params=None, aa_repo_match=None):
template_params = {}
if aa_repo_match is None:
- aa_repo_match = lambda f: False
+ def aa_repo_match(x):
+ return False
errorlist = []
for ent in srclist:
diff --git a/cloudinit/config/cc_apt_pipelining.py b/cloudinit/config/cc_apt_pipelining.py
index e5629175..40c32c84 100644
--- a/cloudinit/config/cc_apt_pipelining.py
+++ b/cloudinit/config/cc_apt_pipelining.py
@@ -43,7 +43,7 @@ def handle(_name, cfg, _cloud, log, _args):
write_apt_snippet("0", log, DEFAULT_FILE)
elif apt_pipe_value_s in ("none", "unchanged", "os"):
return
- elif apt_pipe_value_s in [str(b) for b in xrange(0, 6)]:
+ elif apt_pipe_value_s in [str(b) for b in range(0, 6)]:
write_apt_snippet(apt_pipe_value_s, log, DEFAULT_FILE)
else:
log.warn("Invalid option for apt_pipeling: %s", apt_pipe_value)
diff --git a/cloudinit/config/cc_bootcmd.py b/cloudinit/config/cc_bootcmd.py
index 3ac22967..a295cc4e 100644
--- a/cloudinit/config/cc_bootcmd.py
+++ b/cloudinit/config/cc_bootcmd.py
@@ -36,7 +36,7 @@ def handle(name, cfg, cloud, log, _args):
with util.ExtendedTemporaryFile(suffix=".sh") as tmpf:
try:
content = util.shellify(cfg["bootcmd"])
- tmpf.write(content)
+ tmpf.write(util.encode_text(content))
tmpf.flush()
except:
util.logexc(log, "Failed to shellify bootcmd")
diff --git a/cloudinit/config/cc_ca_certs.py b/cloudinit/config/cc_ca_certs.py
index 4f2a46a1..8248b020 100644
--- a/cloudinit/config/cc_ca_certs.py
+++ b/cloudinit/config/cc_ca_certs.py
@@ -44,7 +44,7 @@ def add_ca_certs(certs):
if certs:
# First ensure they are strings...
cert_file_contents = "\n".join([str(c) for c in certs])
- util.write_file(CA_CERT_FULL_PATH, cert_file_contents, mode=0644)
+ util.write_file(CA_CERT_FULL_PATH, cert_file_contents, mode=0o644)
# Append cert filename to CA_CERT_CONFIG file.
# We have to strip the content because blank lines in the file
@@ -63,7 +63,7 @@ def remove_default_ca_certs():
"""
util.delete_dir_contents(CA_CERT_PATH)
util.delete_dir_contents(CA_CERT_SYSTEM_PATH)
- util.write_file(CA_CERT_CONFIG, "", mode=0644)
+ util.write_file(CA_CERT_CONFIG, "", mode=0o644)
debconf_sel = "ca-certificates ca-certificates/trust_new_crts select no"
util.subp(('debconf-set-selections', '-'), debconf_sel)
diff --git a/cloudinit/config/cc_chef.py b/cloudinit/config/cc_chef.py
index fc837363..e18c5405 100644
--- a/cloudinit/config/cc_chef.py
+++ b/cloudinit/config/cc_chef.py
@@ -76,6 +76,8 @@ from cloudinit import templater
from cloudinit import url_helper
from cloudinit import util
+import six
+
RUBY_VERSION_DEFAULT = "1.8"
CHEF_DIRS = tuple([
@@ -261,7 +263,7 @@ def run_chef(chef_cfg, log):
cmd_args = chef_cfg['exec_arguments']
if isinstance(cmd_args, (list, tuple)):
cmd.extend(cmd_args)
- elif isinstance(cmd_args, (str, basestring)):
+ elif isinstance(cmd_args, six.string_types):
cmd.append(cmd_args)
else:
log.warn("Unknown type %s provided for chef"
@@ -300,7 +302,7 @@ def install_chef(cloud, chef_cfg, log):
with util.tempdir() as tmpd:
# Use tmpdir over tmpfile to avoid 'text file busy' on execute
tmpf = "%s/chef-omnibus-install" % tmpd
- util.write_file(tmpf, str(content), mode=0700)
+ util.write_file(tmpf, content, mode=0o700)
util.subp([tmpf], capture=False)
else:
log.warn("Unknown chef install type '%s'", install_type)
diff --git a/cloudinit/config/cc_debug.py b/cloudinit/config/cc_debug.py
index 8c489426..bdc32fe6 100644
--- a/cloudinit/config/cc_debug.py
+++ b/cloudinit/config/cc_debug.py
@@ -34,7 +34,8 @@ It can be configured with the following option structure::
"""
import copy
-from StringIO import StringIO
+
+from six import StringIO
from cloudinit import type_utils
from cloudinit import util
@@ -77,7 +78,7 @@ def handle(name, cfg, cloud, log, args):
dump_cfg = copy.deepcopy(cfg)
for k in SKIP_KEYS:
dump_cfg.pop(k, None)
- all_keys = list(dump_cfg.keys())
+ all_keys = list(dump_cfg)
for k in all_keys:
if k.startswith("_"):
dump_cfg.pop(k, None)
@@ -103,6 +104,6 @@ def handle(name, cfg, cloud, log, args):
line = "ci-info: %s\n" % (line)
content_to_file.append(line)
if out_file:
- util.write_file(out_file, "".join(content_to_file), 0644, "w")
+ util.write_file(out_file, "".join(content_to_file), 0o644, "w")
else:
util.multi_log("".join(content_to_file), console=True, stderr=False)
diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py
index 1660832b..0ecc2e4c 100644
--- a/cloudinit/config/cc_disk_setup.py
+++ b/cloudinit/config/cc_disk_setup.py
@@ -27,6 +27,7 @@ frequency = PER_INSTANCE
# Define the commands to use
UDEVADM_CMD = util.which('udevadm')
SFDISK_CMD = util.which("sfdisk")
+SGDISK_CMD = util.which("sgdisk")
LSBLK_CMD = util.which("lsblk")
BLKID_CMD = util.which("blkid")
BLKDEV_CMD = util.which("blockdev")
@@ -151,7 +152,7 @@ def enumerate_disk(device, nodeps=False):
name: the device name, i.e. sda
"""
- lsblk_cmd = [LSBLK_CMD, '--pairs', '--out', 'NAME,TYPE,FSTYPE,LABEL',
+ lsblk_cmd = [LSBLK_CMD, '--pairs', '--output', 'NAME,TYPE,FSTYPE,LABEL',
device]
if nodeps:
@@ -166,11 +167,12 @@ def enumerate_disk(device, nodeps=False):
parts = [x for x in (info.strip()).splitlines() if len(x.split()) > 0]
for part in parts:
- d = {'name': None,
- 'type': None,
- 'fstype': None,
- 'label': None,
- }
+ d = {
+ 'name': None,
+ 'type': None,
+ 'fstype': None,
+ 'label': None,
+ }
for key, value in value_splitter(part):
d[key.lower()] = value
@@ -303,8 +305,7 @@ def is_disk_used(device):
# If the child count is higher 1, then there are child nodes
# such as partition or device mapper nodes
- use_count = [x for x in enumerate_disk(device)]
- if len(use_count.splitlines()) > 1:
+ if len(list(enumerate_disk(device))) > 1:
return True
# If we see a file system, then its used
@@ -315,22 +316,6 @@ def is_disk_used(device):
return False
-def get_hdd_size(device):
- """
- Returns the hard disk size.
- This works with any disk type, including GPT.
- """
-
- size_cmd = [SFDISK_CMD, '--show-size', device]
- size = None
- try:
- size, _err = util.subp(size_cmd)
- except Exception as e:
- raise Exception("Failed to get %s size\n%s" % (device, e))
-
- return int(size.strip())
-
-
def get_dyn_func(*args):
"""
Call the appropriate function.
@@ -358,6 +343,30 @@ def get_dyn_func(*args):
raise Exception("No such function %s to call!" % func_name)
+def get_mbr_hdd_size(device):
+ size_cmd = [SFDISK_CMD, '--show-size', device]
+ size = None
+ try:
+ size, _err = util.subp(size_cmd)
+ except Exception as e:
+ raise Exception("Failed to get %s size\n%s" % (device, e))
+
+ return int(size.strip())
+
+
+def get_gpt_hdd_size(device):
+ out, _ = util.subp([SGDISK_CMD, '-p', device])
+ return out.splitlines()[0].split()[2]
+
+
+def get_hdd_size(table_type, device):
+ """
+ Returns the hard disk size.
+ This works with any disk type, including GPT.
+ """
+ return get_dyn_func("get_%s_hdd_size", table_type, device)
+
+
def check_partition_mbr_layout(device, layout):
"""
Returns true if the partition layout matches the one on the disk
@@ -393,6 +402,36 @@ def check_partition_mbr_layout(device, layout):
break
found_layout.append(type_label)
+ return found_layout
+
+
+def check_partition_gpt_layout(device, layout):
+ prt_cmd = [SGDISK_CMD, '-p', device]
+ try:
+ out, _err = util.subp(prt_cmd)
+ except Exception as e:
+ raise Exception("Error running partition command on %s\n%s" % (
+ device, e))
+
+ out_lines = iter(out.splitlines())
+ # Skip header
+ for line in out_lines:
+ if line.strip().startswith('Number'):
+ break
+
+ return [line.strip().split()[-1] for line in out_lines]
+
+
+def check_partition_layout(table_type, device, layout):
+ """
+ See if the partition lay out matches.
+
+ This is future a future proofing function. In order
+ to add support for other disk layout schemes, add a
+ function called check_partition_%s_layout
+ """
+ found_layout = get_dyn_func(
+ "check_partition_%s_layout", table_type, device, layout)
if isinstance(layout, bool):
# if we are using auto partitioning, or "True" be happy
@@ -417,18 +456,6 @@ def check_partition_mbr_layout(device, layout):
return False
-def check_partition_layout(table_type, device, layout):
- """
- See if the partition lay out matches.
-
- This is future a future proofing function. In order
- to add support for other disk layout schemes, add a
- function called check_partition_%s_layout
- """
- return get_dyn_func("check_partition_%s_layout", table_type, device,
- layout)
-
-
def get_partition_mbr_layout(size, layout):
"""
Calculate the layout of the partition table. Partition sizes
@@ -481,6 +508,29 @@ def get_partition_mbr_layout(size, layout):
return sfdisk_definition
+def get_partition_gpt_layout(size, layout):
+ if isinstance(layout, bool):
+ return [(None, [0, 0])]
+
+ partition_specs = []
+ for partition in layout:
+ if isinstance(partition, list):
+ if len(partition) != 2:
+ raise Exception(
+ "Partition was incorrectly defined: %s" % partition)
+ percent, partition_type = partition
+ else:
+ percent = partition
+ partition_type = None
+
+ part_size = int(float(size) * (float(percent) / 100))
+ partition_specs.append((partition_type, [0, '+{}'.format(part_size)]))
+
+ # The last partition should use up all remaining space
+ partition_specs[-1][-1][-1] = 0
+ return partition_specs
+
+
def purge_disk_ptable(device):
# wipe the first and last megabyte of a disk (or file)
# gpt stores partition table both at front and at end.
@@ -556,6 +606,22 @@ def exec_mkpart_mbr(device, layout):
read_parttbl(device)
+def exec_mkpart_gpt(device, layout):
+ try:
+ util.subp([SGDISK_CMD, '-Z', device])
+ for index, (partition_type, (start, end)) in enumerate(layout):
+ index += 1
+ util.subp([SGDISK_CMD,
+ '-n', '{}:{}:{}'.format(index, start, end), device])
+ if partition_type is not None:
+ util.subp(
+ [SGDISK_CMD,
+ '-t', '{}:{}'.format(index, partition_type), device])
+ except Exception:
+ LOG.warn("Failed to partition device %s" % device)
+ raise
+
+
def exec_mkpart(table_type, device, layout):
"""
Fetches the function for creating the table type.
@@ -583,6 +649,8 @@ def mkpart(device, definition):
table_type: Which partition table to use, defaults to MBR
device: the device to work on.
"""
+ # ensure that we get a real device rather than a symbolic link
+ device = os.path.realpath(device)
LOG.debug("Checking values for %s definition" % device)
overwrite = definition.get('overwrite', False)
@@ -618,7 +686,7 @@ def mkpart(device, definition):
return
LOG.debug("Checking for device size")
- device_size = get_hdd_size(device)
+ device_size = get_hdd_size(table_type, device)
LOG.debug("Calculating partition layout")
part_definition = get_partition_layout(table_type, device_size, layout)
@@ -634,11 +702,12 @@ def lookup_force_flag(fs):
"""
A force flag might be -F or -F, this look it up
"""
- flags = {'ext': '-F',
- 'btrfs': '-f',
- 'xfs': '-f',
- 'reiserfs': '-f',
- }
+ flags = {
+ 'ext': '-F',
+ 'btrfs': '-f',
+ 'xfs': '-f',
+ 'reiserfs': '-f',
+ }
if 'ext' in fs.lower():
fs = 'ext'
@@ -680,6 +749,9 @@ def mkfs(fs_cfg):
fs_replace = fs_cfg.get('replace_fs', False)
overwrite = fs_cfg.get('overwrite', False)
+ # ensure that we get a real device rather than a symbolic link
+ device = os.path.realpath(device)
+
# This allows you to define the default ephemeral or swap
LOG.debug("Checking %s against default devices", device)
@@ -754,10 +826,11 @@ def mkfs(fs_cfg):
# Create the commands
if fs_cmd:
- fs_cmd = fs_cfg['cmd'] % {'label': label,
- 'filesystem': fs_type,
- 'device': device,
- }
+ fs_cmd = fs_cfg['cmd'] % {
+ 'label': label,
+ 'filesystem': fs_type,
+ 'device': device,
+ }
else:
# Find the mkfs command
mkfs_cmd = util.which("mkfs.%s" % fs_type)
diff --git a/cloudinit/config/cc_emit_upstart.py b/cloudinit/config/cc_emit_upstart.py
index 6d376184..86ae97ab 100644
--- a/cloudinit/config/cc_emit_upstart.py
+++ b/cloudinit/config/cc_emit_upstart.py
@@ -21,11 +21,31 @@
import os
from cloudinit.settings import PER_ALWAYS
+from cloudinit import log as logging
from cloudinit import util
frequency = PER_ALWAYS
distros = ['ubuntu', 'debian']
+LOG = logging.getLogger(__name__)
+
+
+def is_upstart_system():
+ if not os.path.isfile("/sbin/initctl"):
+ LOG.debug("no /sbin/initctl located")
+ return False
+
+ myenv = os.environ.copy()
+ if 'UPSTART_SESSION' in myenv:
+ del myenv['UPSTART_SESSION']
+ check_cmd = ['initctl', 'version']
+ try:
+ (out, err) = util.subp(check_cmd, env=myenv)
+ return 'upstart' in out
+ except util.ProcessExecutionError as e:
+ LOG.debug("'%s' returned '%s', not using upstart",
+ ' '.join(check_cmd), e.exit_code)
+ return False
def handle(name, _cfg, cloud, log, args):
@@ -34,10 +54,11 @@ def handle(name, _cfg, cloud, log, args):
# Default to the 'cloud-config'
# event for backwards compat.
event_names = ['cloud-config']
- if not os.path.isfile("/sbin/initctl"):
- log.debug(("Skipping module named %s,"
- " no /sbin/initctl located"), name)
+
+ if not is_upstart_system():
+ log.debug("not upstart system, '%s' disabled")
return
+
cfgpath = cloud.paths.get_ipath_cur("cloud_config")
for n in event_names:
cmd = ['initctl', 'emit', str(n), 'CLOUD_CFG=%s' % cfgpath]
diff --git a/cloudinit/config/cc_fan.py b/cloudinit/config/cc_fan.py
new file mode 100644
index 00000000..39e3850e
--- /dev/null
+++ b/cloudinit/config/cc_fan.py
@@ -0,0 +1,101 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+#
+# Author: Scott Moser <scott.moser@canonical.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+fan module allows configuration of Ubuntu Fan
+ https://wiki.ubuntu.com/FanNetworking
+
+Example config:
+ #cloud-config
+ fan:
+ config: |
+ # fan 240
+ 10.0.0.0/8 eth0/16 dhcp
+ 10.0.0.0/8 eth1/16 dhcp off
+ # fan 241
+ 241.0.0.0/8 eth0/16 dhcp
+ config_path: /etc/network/fan
+
+If cloud-init sees a 'fan' entry in cloud-config it will
+ a.) write 'config_path' with the contents
+ b.) install the package 'ubuntu-fan' if it is not installed
+ c.) ensure the service is started (or restarted if was previously running)
+"""
+
+from cloudinit import log as logging
+from cloudinit import util
+from cloudinit.settings import PER_INSTANCE
+
+LOG = logging.getLogger(__name__)
+
+frequency = PER_INSTANCE
+
+BUILTIN_CFG = {
+ 'config': None,
+ 'config_path': '/etc/network/fan',
+}
+
+
+def stop_update_start(service, config_file, content, systemd=False):
+ if systemd:
+ cmds = {'stop': ['systemctl', 'stop', service],
+ 'start': ['systemctl', 'start', service],
+ 'enable': ['systemctl', 'enable', service]}
+ else:
+ cmds = {'stop': ['service', 'stop'],
+ 'start': ['service', 'start']}
+
+ def run(cmd, msg):
+ try:
+ return util.subp(cmd, capture=True)
+ except util.ProcessExecutionError as e:
+ LOG.warn("failed: %s (%s): %s", service, cmd, e)
+ return False
+
+ stop_failed = not run(cmds['stop'], msg='stop %s' % service)
+ if not content.endswith('\n'):
+ content += '\n'
+ util.write_file(config_file, content, omode="w")
+
+ ret = run(cmds['start'], msg='start %s' % service)
+ if ret and stop_failed:
+ LOG.warn("success: %s started", service)
+
+ if 'enable' in cmds:
+ ret = run(cmds['enable'], msg='enable %s' % service)
+
+ return ret
+
+
+def handle(name, cfg, cloud, log, args):
+ cfgin = cfg.get('fan')
+ if not cfgin:
+ cfgin = {}
+ mycfg = util.mergemanydict([cfgin, BUILTIN_CFG])
+
+ if not mycfg.get('config'):
+ LOG.debug("%s: no 'fan' config entry. disabling", name)
+ return
+
+ util.write_file(mycfg.get('config_path'), mycfg.get('config'), omode="w")
+ distro = cloud.distro
+ if not util.which('fanctl'):
+ distro.install_packages(['ubuntu-fan'])
+
+ stop_update_start(
+ service='ubuntu-fan', config_file=mycfg.get('config_path'),
+ content=mycfg.get('config'), systemd=distro.uses_systemd())
diff --git a/cloudinit/config/cc_final_message.py b/cloudinit/config/cc_final_message.py
index b24294e4..4a51476f 100644
--- a/cloudinit/config/cc_final_message.py
+++ b/cloudinit/config/cc_final_message.py
@@ -26,9 +26,12 @@ from cloudinit.settings import PER_ALWAYS
frequency = PER_ALWAYS
-# Cheetah formated default message
-FINAL_MESSAGE_DEF = ("Cloud-init v. ${version} finished at ${timestamp}."
- " Datasource ${datasource}. Up ${uptime} seconds")
+# Jinja formated default message
+FINAL_MESSAGE_DEF = (
+ "## template: jinja\n"
+ "Cloud-init v. {{version}} finished at {{timestamp}}."
+ " Datasource {{datasource}}. Up {{uptime}} seconds"
+)
def handle(_name, cfg, cloud, log, args):
diff --git a/cloudinit/config/cc_growpart.py b/cloudinit/config/cc_growpart.py
index f52c41f0..859d69f1 100644
--- a/cloudinit/config/cc_growpart.py
+++ b/cloudinit/config/cc_growpart.py
@@ -276,7 +276,7 @@ def handle(_name, cfg, _cloud, log, _args):
log.debug("use ignore_growroot_disabled to ignore")
return
- devices = util.get_cfg_option_list(cfg, "devices", ["/"])
+ devices = util.get_cfg_option_list(mycfg, "devices", ["/"])
if not len(devices):
log.debug("growpart: empty device list")
return
diff --git a/cloudinit/config/cc_grub_dpkg.py b/cloudinit/config/cc_grub_dpkg.py
index e3219e81..3c2d9985 100644
--- a/cloudinit/config/cc_grub_dpkg.py
+++ b/cloudinit/config/cc_grub_dpkg.py
@@ -25,19 +25,23 @@ from cloudinit import util
distros = ['ubuntu', 'debian']
-def handle(_name, cfg, _cloud, log, _args):
- idevs = None
- idevs_empty = None
+def handle(name, cfg, _cloud, log, _args):
- if "grub-dpkg" in cfg:
- idevs = util.get_cfg_option_str(cfg["grub-dpkg"],
- "grub-pc/install_devices", None)
- idevs_empty = util.get_cfg_option_str(cfg["grub-dpkg"],
- "grub-pc/install_devices_empty", None)
+ mycfg = cfg.get("grub_dpkg", cfg.get("grub-dpkg", {}))
+ if not mycfg:
+ mycfg = {}
+
+ enabled = mycfg.get('enabled', True)
+ if util.is_false(enabled):
+ log.debug("%s disabled by config grub_dpkg/enabled=%s", name, enabled)
+ return
+
+ idevs = util.get_cfg_option_str(mycfg, "grub-pc/install_devices", None)
+ idevs_empty = util.get_cfg_option_str(
+ mycfg, "grub-pc/install_devices_empty", None)
if ((os.path.exists("/dev/sda1") and not os.path.exists("/dev/sda")) or
- (os.path.exists("/dev/xvda1")
- and not os.path.exists("/dev/xvda"))):
+ (os.path.exists("/dev/xvda1") and not os.path.exists("/dev/xvda"))):
if idevs is None:
idevs = ""
if idevs_empty is None:
@@ -61,7 +65,7 @@ def handle(_name, cfg, _cloud, log, _args):
(idevs, idevs_empty))
log.debug("Setting grub debconf-set-selections with '%s','%s'" %
- (idevs, idevs_empty))
+ (idevs, idevs_empty))
try:
util.subp(['debconf-set-selections'], dconf_sel)
diff --git a/cloudinit/config/cc_keys_to_console.py b/cloudinit/config/cc_keys_to_console.py
index f1c1adff..aa844ee9 100644
--- a/cloudinit/config/cc_keys_to_console.py
+++ b/cloudinit/config/cc_keys_to_console.py
@@ -48,7 +48,7 @@ def handle(name, cfg, cloud, log, _args):
"ssh_fp_console_blacklist", [])
key_blacklist = util.get_cfg_option_list(cfg,
"ssh_key_console_blacklist",
- ["ssh-dss"])
+ ["ssh-dss"])
try:
cmd = [helper_path]
diff --git a/cloudinit/config/cc_landscape.py b/cloudinit/config/cc_landscape.py
index 8a709677..68fcb27f 100644
--- a/cloudinit/config/cc_landscape.py
+++ b/cloudinit/config/cc_landscape.py
@@ -20,7 +20,7 @@
import os
-from StringIO import StringIO
+from six import StringIO
from configobj import ConfigObj
@@ -38,12 +38,12 @@ distros = ['ubuntu']
# defaults taken from stock client.conf in landscape-client 11.07.1.1-0ubuntu2
LSC_BUILTIN_CFG = {
- 'client': {
- 'log_level': "info",
- 'url': "https://landscape.canonical.com/message-system",
- 'ping_url': "http://landscape.canonical.com/ping",
- 'data_path': "/var/lib/landscape/client",
- }
+ 'client': {
+ 'log_level': "info",
+ 'url': "https://landscape.canonical.com/message-system",
+ 'ping_url': "http://landscape.canonical.com/ping",
+ 'data_path': "/var/lib/landscape/client",
+ }
}
diff --git a/cloudinit/config/cc_locale.py b/cloudinit/config/cc_locale.py
index 6feaae9d..bbe5fcae 100644
--- a/cloudinit/config/cc_locale.py
+++ b/cloudinit/config/cc_locale.py
@@ -27,9 +27,9 @@ def handle(name, cfg, cloud, log, args):
else:
locale = util.get_cfg_option_str(cfg, "locale", cloud.get_locale())
- if not locale:
- log.debug(("Skipping module named %s, "
- "no 'locale' configuration found"), name)
+ if util.is_false(locale):
+ log.debug("Skipping module named %s, disabled by config: %s",
+ name, locale)
return
log.debug("Setting locale to %s", locale)
diff --git a/cloudinit/config/cc_lxd.py b/cloudinit/config/cc_lxd.py
new file mode 100644
index 00000000..63b8fb63
--- /dev/null
+++ b/cloudinit/config/cc_lxd.py
@@ -0,0 +1,85 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2016 Canonical Ltd.
+#
+# Author: Wesley Wiedenmeier <wesley.wiedenmeier@canonical.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+This module initializes lxd using 'lxd init'
+
+Example config:
+ #cloud-config
+ lxd:
+ init:
+ network_address: <ip addr>
+ network_port: <port>
+ storage_backend: <zfs/dir>
+ storage_create_device: <dev>
+ storage_create_loop: <size>
+ storage_pool: <name>
+ trust_password: <password>
+"""
+
+from cloudinit import util
+
+
+def handle(name, cfg, cloud, log, args):
+ # Get config
+ lxd_cfg = cfg.get('lxd')
+ if not lxd_cfg:
+ log.debug("Skipping module named %s, not present or disabled by cfg")
+ return
+ if not isinstance(lxd_cfg, dict):
+ log.warn("lxd config must be a dictionary. found a '%s'",
+ type(lxd_cfg))
+ return
+
+ init_cfg = lxd_cfg.get('init')
+ if not isinstance(init_cfg, dict):
+ log.warn("lxd/init config must be a dictionary. found a '%s'",
+ type(init_cfg))
+ init_cfg = {}
+
+ if not init_cfg:
+ log.debug("no lxd/init config. disabled.")
+ return
+
+ packages = []
+ # Ensure lxd is installed
+ if not util.which("lxd"):
+ packages.append('lxd')
+
+ # if using zfs, get the utils
+ if init_cfg.get("storage_backend") == "zfs" and not util.which('zfs'):
+ packages.append('zfs')
+
+ if len(packages):
+ try:
+ cloud.distro.install_packages(packages)
+ except util.ProcessExecutionError as exc:
+ log.warn("failed to install packages %s: %s", packages, exc)
+ return
+
+ # Set up lxd if init config is given
+ init_keys = (
+ 'network_address', 'network_port', 'storage_backend',
+ 'storage_create_device', 'storage_create_loop',
+ 'storage_pool', 'trust_password')
+ cmd = ['lxd', 'init', '--auto']
+ for k in init_keys:
+ if init_cfg.get(k):
+ cmd.extend(["--%s=%s" %
+ (k.replace('_', '-'), str(init_cfg[k]))])
+ util.subp(cmd)
diff --git a/cloudinit/config/cc_mcollective.py b/cloudinit/config/cc_mcollective.py
index b670390d..425420ae 100644
--- a/cloudinit/config/cc_mcollective.py
+++ b/cloudinit/config/cc_mcollective.py
@@ -19,7 +19,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+import six
+from six import StringIO
# Used since this can maintain comments
# and doesn't need a top level section
@@ -51,17 +52,17 @@ def handle(name, cfg, cloud, log, _args):
# original file in order to be able to mix the rest up
mcollective_config = ConfigObj(SERVER_CFG)
# See: http://tiny.cc/jh9agw
- for (cfg_name, cfg) in mcollective_cfg['conf'].iteritems():
+ for (cfg_name, cfg) in mcollective_cfg['conf'].items():
if cfg_name == 'public-cert':
- util.write_file(PUBCERT_FILE, cfg, mode=0644)
+ util.write_file(PUBCERT_FILE, cfg, mode=0o644)
mcollective_config['plugin.ssl_server_public'] = PUBCERT_FILE
mcollective_config['securityprovider'] = 'ssl'
elif cfg_name == 'private-cert':
- util.write_file(PRICERT_FILE, cfg, mode=0600)
+ util.write_file(PRICERT_FILE, cfg, mode=0o600)
mcollective_config['plugin.ssl_server_private'] = PRICERT_FILE
mcollective_config['securityprovider'] = 'ssl'
else:
- if isinstance(cfg, (basestring, str)):
+ if isinstance(cfg, six.string_types):
# Just set it in the 'main' section
mcollective_config[cfg_name] = cfg
elif isinstance(cfg, (dict)):
@@ -69,7 +70,7 @@ def handle(name, cfg, cloud, log, _args):
# if it is needed and then add/or create items as needed
if cfg_name not in mcollective_config.sections:
mcollective_config[cfg_name] = {}
- for (o, v) in cfg.iteritems():
+ for (o, v) in cfg.items():
mcollective_config[cfg_name][o] = v
else:
# Otherwise just try to convert it to a string
@@ -81,7 +82,7 @@ def handle(name, cfg, cloud, log, _args):
contents = StringIO()
mcollective_config.write(contents)
contents = contents.getvalue()
- util.write_file(SERVER_CFG, contents, mode=0644)
+ util.write_file(SERVER_CFG, contents, mode=0o644)
# Start mcollective
util.subp(['service', 'mcollective', 'start'], capture=False)
diff --git a/cloudinit/config/cc_mounts.py b/cloudinit/config/cc_mounts.py
index 1cb1e839..4fe3ee21 100644
--- a/cloudinit/config/cc_mounts.py
+++ b/cloudinit/config/cc_mounts.py
@@ -28,15 +28,15 @@ from cloudinit import type_utils
from cloudinit import util
# Shortname matches 'sda', 'sda1', 'xvda', 'hda', 'sdb', xvdb, vda, vdd1, sr0
-SHORTNAME_FILTER = r"^([x]{0,1}[shv]d[a-z][0-9]*|sr[0-9]+)$"
-SHORTNAME = re.compile(SHORTNAME_FILTER)
+DEVICE_NAME_FILTER = r"^([x]{0,1}[shv]d[a-z][0-9]*|sr[0-9]+)$"
+DEVICE_NAME_RE = re.compile(DEVICE_NAME_FILTER)
WS = re.compile("[%s]+" % (whitespace))
FSTAB_PATH = "/etc/fstab"
LOG = logging.getLogger(__name__)
-def is_mdname(name):
+def is_meta_device_name(name):
# return true if this is a metadata service name
if name in ["ami", "root", "swap"]:
return True
@@ -48,6 +48,25 @@ def is_mdname(name):
return False
+def _get_nth_partition_for_device(device_path, partition_number):
+ potential_suffixes = [str(partition_number), 'p%s' % (partition_number,),
+ '-part%s' % (partition_number,)]
+ for suffix in potential_suffixes:
+ potential_partition_device = '%s%s' % (device_path, suffix)
+ if os.path.exists(potential_partition_device):
+ return potential_partition_device
+ return None
+
+
+def _is_block_device(device_path, partition_path=None):
+ device_name = os.path.realpath(device_path).split('/')[-1]
+ sys_path = os.path.join('/sys/block/', device_name)
+ if partition_path is not None:
+ sys_path = os.path.join(
+ sys_path, os.path.realpath(partition_path).split('/')[-1])
+ return os.path.exists(sys_path)
+
+
def sanitize_devname(startname, transformer, log):
log.debug("Attempting to determine the real name of %s", startname)
@@ -58,21 +77,34 @@ def sanitize_devname(startname, transformer, log):
devname = "ephemeral0"
log.debug("Adjusted mount option from ephemeral to ephemeral0")
- (blockdev, part) = util.expand_dotted_devname(devname)
+ device_path, partition_number = util.expand_dotted_devname(devname)
- if is_mdname(blockdev):
- orig = blockdev
- blockdev = transformer(blockdev)
- if not blockdev:
+ if is_meta_device_name(device_path):
+ orig = device_path
+ device_path = transformer(device_path)
+ if not device_path:
return None
- if not blockdev.startswith("/"):
- blockdev = "/dev/%s" % blockdev
- log.debug("Mapped metadata name %s to %s", orig, blockdev)
+ if not device_path.startswith("/"):
+ device_path = "/dev/%s" % (device_path,)
+ log.debug("Mapped metadata name %s to %s", orig, device_path)
+ else:
+ if DEVICE_NAME_RE.match(startname):
+ device_path = "/dev/%s" % (device_path,)
+
+ partition_path = None
+ if partition_number is None:
+ partition_path = _get_nth_partition_for_device(device_path, 1)
else:
- if SHORTNAME.match(startname):
- blockdev = "/dev/%s" % blockdev
+ partition_path = _get_nth_partition_for_device(device_path,
+ partition_number)
+ if partition_path is None:
+ return None
- return devnode_for_dev_part(blockdev, part)
+ if _is_block_device(device_path, partition_path):
+ if partition_path is not None:
+ return partition_path
+ return device_path
+ return None
def suggested_swapsize(memsize=None, maxsize=None, fsys=None):
@@ -172,11 +204,12 @@ def setup_swapfile(fname, size=None, maxsize=None):
try:
util.ensure_dir(tdir)
util.log_time(LOG.debug, msg, func=util.subp,
- args=[['sh', '-c',
- ('rm -f "$1" && umask 0066 && '
- 'dd if=/dev/zero "of=$1" bs=1M "count=$2" && '
- 'mkswap "$1" || { r=$?; rm -f "$1"; exit $r; }'),
- 'setup_swap', fname, mbsize]])
+ args=[['sh', '-c',
+ ('rm -f "$1" && umask 0066 && '
+ '{ fallocate -l "${2}M" "$1" || '
+ ' dd if=/dev/zero "of=$1" bs=1M "count=$2"; } && '
+ 'mkswap "$1" || { r=$?; rm -f "$1"; exit $r; }'),
+ 'setup_swap', fname, mbsize]])
except Exception as e:
raise IOError("Failed %s: %s" % (msg, e))
@@ -230,7 +263,11 @@ def handle_swapcfg(swapcfg):
def handle(_name, cfg, cloud, log, _args):
# fs_spec, fs_file, fs_vfstype, fs_mntops, fs-freq, fs_passno
- defvals = [None, None, "auto", "defaults,nobootwait", "0", "2"]
+ def_mnt_opts = "defaults,nobootwait"
+ if cloud.distro.uses_systemd():
+ def_mnt_opts = "defaults,nofail"
+
+ defvals = [None, None, "auto", def_mnt_opts, "0", "2"]
defvals = cfg.get("mount_default_fields", defvals)
# these are our default set of mounts
@@ -366,49 +403,3 @@ def handle(_name, cfg, cloud, log, _args):
util.subp(("mount", "-a"))
except:
util.logexc(log, "Activating mounts via 'mount -a' failed")
-
-
-def devnode_for_dev_part(device, partition):
- """
- Find the name of the partition. While this might seem rather
- straight forward, its not since some devices are '<device><partition>'
- while others are '<device>p<partition>'. For example, /dev/xvda3 on EC2
- will present as /dev/xvda3p1 for the first partition since /dev/xvda3 is
- a block device.
- """
- if not os.path.exists(device):
- return None
-
- short_name = os.path.basename(device)
- sys_path = "/sys/block/%s" % short_name
-
- if not os.path.exists(sys_path):
- LOG.debug("did not find entry for %s in /sys/block", short_name)
- return None
-
- sys_long_path = sys_path + "/" + short_name
-
- if partition is not None:
- partition = str(partition)
-
- if partition is None:
- valid_mappings = [sys_long_path + "1", sys_long_path + "p1"]
- elif partition != "0":
- valid_mappings = [sys_long_path + "%s" % partition,
- sys_long_path + "p%s" % partition]
- else:
- valid_mappings = []
-
- for cdisk in valid_mappings:
- if not os.path.exists(cdisk):
- continue
-
- dev_path = "/dev/%s" % os.path.basename(cdisk)
- if os.path.exists(dev_path):
- return dev_path
-
- if partition is None or partition == "0":
- return device
-
- LOG.debug("Did not fine partition %s for device %s", partition, device)
- return None
diff --git a/cloudinit/config/cc_phone_home.py b/cloudinit/config/cc_phone_home.py
index 5bc68b83..18a7ddad 100644
--- a/cloudinit/config/cc_phone_home.py
+++ b/cloudinit/config/cc_phone_home.py
@@ -81,7 +81,7 @@ def handle(name, cfg, cloud, log, args):
'pub_key_ecdsa': '/etc/ssh/ssh_host_ecdsa_key.pub',
}
- for (n, path) in pubkeys.iteritems():
+ for (n, path) in pubkeys.items():
try:
all_keys[n] = util.load_file(path)
except:
@@ -99,7 +99,7 @@ def handle(name, cfg, cloud, log, args):
# Get them read to be posted
real_submit_keys = {}
- for (k, v) in submit_keys.iteritems():
+ for (k, v) in submit_keys.items():
if v is None:
real_submit_keys[k] = 'N/A'
else:
diff --git a/cloudinit/config/cc_power_state_change.py b/cloudinit/config/cc_power_state_change.py
index 09d37371..cc3f7f70 100644
--- a/cloudinit/config/cc_power_state_change.py
+++ b/cloudinit/config/cc_power_state_change.py
@@ -22,6 +22,7 @@ from cloudinit import util
import errno
import os
import re
+import six
import subprocess
import time
@@ -48,10 +49,40 @@ def givecmdline(pid):
return None
+def check_condition(cond, log=None):
+ if isinstance(cond, bool):
+ if log:
+ log.debug("Static Condition: %s" % cond)
+ return cond
+
+ pre = "check_condition command (%s): " % cond
+ try:
+ proc = subprocess.Popen(cond, shell=not isinstance(cond, list))
+ proc.communicate()
+ ret = proc.returncode
+ if ret == 0:
+ if log:
+ log.debug(pre + "exited 0. condition met.")
+ return True
+ elif ret == 1:
+ if log:
+ log.debug(pre + "exited 1. condition not met.")
+ return False
+ else:
+ if log:
+ log.warn(pre + "unexpected exit %s. " % ret +
+ "do not apply change.")
+ return False
+ except Exception as e:
+ if log:
+ log.warn(pre + "Unexpected error: %s" % e)
+ return False
+
+
def handle(_name, cfg, _cloud, log, _args):
try:
- (args, timeout) = load_power_state(cfg)
+ (args, timeout, condition) = load_power_state(cfg)
if args is None:
log.debug("no power_state provided. doing nothing")
return
@@ -59,6 +90,10 @@ def handle(_name, cfg, _cloud, log, _args):
log.warn("%s Not performing power state change!" % str(e))
return
+ if condition is False:
+ log.debug("Condition was false. Will not perform state change.")
+ return
+
mypid = os.getpid()
cmdline = givecmdline(mypid)
@@ -70,8 +105,8 @@ def handle(_name, cfg, _cloud, log, _args):
log.debug("After pid %s ends, will execute: %s" % (mypid, ' '.join(args)))
- util.fork_cb(run_after_pid_gone, mypid, cmdline, timeout, log, execmd,
- [args, devnull_fp])
+ util.fork_cb(run_after_pid_gone, mypid, cmdline, timeout, log,
+ condition, execmd, [args, devnull_fp])
def load_power_state(cfg):
@@ -80,7 +115,7 @@ def load_power_state(cfg):
pstate = cfg.get('power_state')
if pstate is None:
- return (None, None)
+ return (None, None, None)
if not isinstance(pstate, dict):
raise TypeError("power_state is not a dict.")
@@ -115,7 +150,10 @@ def load_power_state(cfg):
raise ValueError("failed to convert timeout '%s' to float." %
pstate['timeout'])
- return (args, timeout)
+ condition = pstate.get("condition", True)
+ if not isinstance(condition, six.string_types + (list, bool)):
+ raise TypeError("condition type %s invalid. must be list, bool, str")
+ return (args, timeout, condition)
def doexit(sysexit):
@@ -133,7 +171,7 @@ def execmd(exe_args, output=None, data_in=None):
doexit(ret)
-def run_after_pid_gone(pid, pidcmdline, timeout, log, func, args):
+def run_after_pid_gone(pid, pidcmdline, timeout, log, condition, func, args):
# wait until pid, with /proc/pid/cmdline contents of pidcmdline
# is no longer alive. After it is gone, or timeout has passed
# execute func(args)
@@ -175,4 +213,11 @@ def run_after_pid_gone(pid, pidcmdline, timeout, log, func, args):
if log:
log.debug(msg)
+
+ try:
+ if not check_condition(condition, log):
+ return
+ except Exception as e:
+ fatal("Unexpected Exception when checking condition: %s" % e)
+
func(*args)
diff --git a/cloudinit/config/cc_puppet.py b/cloudinit/config/cc_puppet.py
index 471a1a8a..774d3322 100644
--- a/cloudinit/config/cc_puppet.py
+++ b/cloudinit/config/cc_puppet.py
@@ -18,7 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
import os
import socket
@@ -36,8 +36,8 @@ def _autostart_puppet(log):
# Set puppet to automatically start
if os.path.exists('/etc/default/puppet'):
util.subp(['sed', '-i',
- '-e', 's/^START=.*/START=yes/',
- '/etc/default/puppet'], capture=False)
+ '-e', 's/^START=.*/START=yes/',
+ '/etc/default/puppet'], capture=False)
elif os.path.exists('/bin/systemctl'):
util.subp(['/bin/systemctl', 'enable', 'puppet.service'],
capture=False)
@@ -65,7 +65,7 @@ def handle(name, cfg, cloud, log, _args):
" doing nothing."))
elif install:
log.debug(("Attempting to install puppet %s,"),
- version if version else 'latest')
+ version if version else 'latest')
cloud.distro.install_packages(('puppet', version))
# ... and then update the puppet configuration
@@ -81,22 +81,22 @@ def handle(name, cfg, cloud, log, _args):
cleaned_contents = '\n'.join(cleaned_lines)
puppet_config.readfp(StringIO(cleaned_contents),
filename=PUPPET_CONF_PATH)
- for (cfg_name, cfg) in puppet_cfg['conf'].iteritems():
+ for (cfg_name, cfg) in puppet_cfg['conf'].items():
# Cert configuration is a special case
# Dump the puppet master ca certificate in the correct place
if cfg_name == 'ca_cert':
# Puppet ssl sub-directory isn't created yet
# Create it with the proper permissions and ownership
- util.ensure_dir(PUPPET_SSL_DIR, 0771)
+ util.ensure_dir(PUPPET_SSL_DIR, 0o771)
util.chownbyname(PUPPET_SSL_DIR, 'puppet', 'root')
util.ensure_dir(PUPPET_SSL_CERT_DIR)
util.chownbyname(PUPPET_SSL_CERT_DIR, 'puppet', 'root')
- util.write_file(PUPPET_SSL_CERT_PATH, str(cfg))
+ util.write_file(PUPPET_SSL_CERT_PATH, cfg)
util.chownbyname(PUPPET_SSL_CERT_PATH, 'puppet', 'root')
else:
# Iterate throug the config items, we'll use ConfigParser.set
# to overwrite or create new items as needed
- for (o, v) in cfg.iteritems():
+ for (o, v) in cfg.items():
if o == 'certname':
# Expand %f as the fqdn
# TODO(harlowja) should this use the cloud fqdn??
diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py
index cbc07853..2a2a9f59 100644
--- a/cloudinit/config/cc_resizefs.py
+++ b/cloudinit/config/cc_resizefs.py
@@ -166,7 +166,7 @@ def handle(name, cfg, _cloud, log, args):
func=do_resize, args=(resize_cmd, log))
else:
util.log_time(logfunc=log.debug, msg="Resizing",
- func=do_resize, args=(resize_cmd, log))
+ func=do_resize, args=(resize_cmd, log))
action = 'Resized'
if resize_root == NOBLOCK:
diff --git a/cloudinit/config/cc_resolv_conf.py b/cloudinit/config/cc_resolv_conf.py
index bbaa6c63..71d9e3a7 100644
--- a/cloudinit/config/cc_resolv_conf.py
+++ b/cloudinit/config/cc_resolv_conf.py
@@ -66,8 +66,8 @@ def generate_resolv_conf(template_fn, params, target_fname="/etc/resolv.conf"):
false_flags = []
if 'options' in params:
- for key, val in params['options'].iteritems():
- if type(val) == bool:
+ for key, val in params['options'].items():
+ if isinstance(val, bool):
if val:
flags.append(key)
else:
diff --git a/cloudinit/config/cc_rh_subscription.py b/cloudinit/config/cc_rh_subscription.py
new file mode 100644
index 00000000..6087c45c
--- /dev/null
+++ b/cloudinit/config/cc_rh_subscription.py
@@ -0,0 +1,402 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# Author: Brent Baude <bbaude@redhat.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from cloudinit import util
+
+
+def handle(_name, cfg, _cloud, log, _args):
+ sm = SubscriptionManager(cfg)
+ sm.log = log
+ if not sm.is_registered:
+ try:
+ verify, verify_msg = sm._verify_keys()
+ if verify is not True:
+ raise SubscriptionError(verify_msg)
+ cont = sm.rhn_register()
+ if not cont:
+ raise SubscriptionError("Registration failed or did not "
+ "run completely")
+
+ # Splitting up the registration, auto-attach, and servicelevel
+ # commands because the error codes, messages from subman are not
+ # specific enough.
+
+ # Attempt to change the service level
+ if sm.auto_attach and sm.servicelevel is not None:
+ if not sm._set_service_level():
+ raise SubscriptionError("Setting of service-level "
+ "failed")
+ else:
+ sm.log.debug("Completed auto-attach with service level")
+ elif sm.auto_attach:
+ if not sm._set_auto_attach():
+ raise SubscriptionError("Setting auto-attach failed")
+ else:
+ sm.log.debug("Completed auto-attach")
+
+ if sm.pools is not None:
+ if not isinstance(sm.pools, list):
+ pool_fail = "Pools must in the format of a list"
+ raise SubscriptionError(pool_fail)
+
+ return_stat = sm.addPool(sm.pools)
+ if not return_stat:
+ raise SubscriptionError("Unable to attach pools {0}"
+ .format(sm.pools))
+ if (sm.enable_repo is not None) or (sm.disable_repo is not None):
+ return_stat = sm.update_repos(sm.enable_repo, sm.disable_repo)
+ if not return_stat:
+ raise SubscriptionError("Unable to add or remove repos")
+ sm.log_success("rh_subscription plugin completed successfully")
+ except SubscriptionError as e:
+ sm.log_warn(str(e))
+ sm.log_warn("rh_subscription plugin did not complete successfully")
+ else:
+ sm.log_success("System is already registered")
+
+
+class SubscriptionError(Exception):
+ pass
+
+
+class SubscriptionManager(object):
+ valid_rh_keys = ['org', 'activation-key', 'username', 'password',
+ 'disable-repo', 'enable-repo', 'add-pool',
+ 'rhsm-baseurl', 'server-hostname',
+ 'auto-attach', 'service-level']
+
+ def __init__(self, cfg):
+ self.cfg = cfg
+ self.rhel_cfg = self.cfg.get('rh_subscription', {})
+ self.rhsm_baseurl = self.rhel_cfg.get('rhsm-baseurl')
+ self.server_hostname = self.rhel_cfg.get('server-hostname')
+ self.pools = self.rhel_cfg.get('add-pool')
+ self.activation_key = self.rhel_cfg.get('activation-key')
+ self.org = self.rhel_cfg.get('org')
+ self.userid = self.rhel_cfg.get('username')
+ self.password = self.rhel_cfg.get('password')
+ self.auto_attach = self.rhel_cfg.get('auto-attach')
+ self.enable_repo = self.rhel_cfg.get('enable-repo')
+ self.disable_repo = self.rhel_cfg.get('disable-repo')
+ self.servicelevel = self.rhel_cfg.get('service-level')
+ self.subman = ['subscription-manager']
+ self.is_registered = self._is_registered()
+
+ def log_success(self, msg):
+ '''Simple wrapper for logging info messages. Useful for unittests'''
+ self.log.info(msg)
+
+ def log_warn(self, msg):
+ '''Simple wrapper for logging warning messages. Useful for unittests'''
+ self.log.warn(msg)
+
+ def _verify_keys(self):
+ '''
+ Checks that the keys in the rh_subscription dict from the user-data
+ are what we expect.
+ '''
+
+ for k in self.rhel_cfg:
+ if k not in self.valid_rh_keys:
+ bad_key = "{0} is not a valid key for rh_subscription. "\
+ "Valid keys are: "\
+ "{1}".format(k, ', '.join(self.valid_rh_keys))
+ return False, bad_key
+
+ # Check for bad auto-attach value
+ if (self.auto_attach is not None) and \
+ not (util.is_true(self.auto_attach) or
+ util.is_false(self.auto_attach)):
+ not_bool = "The key auto-attach must be a boolean value "\
+ "(True/False "
+ return False, not_bool
+
+ if (self.servicelevel is not None) and ((not self.auto_attach) or
+ (util.is_false(str(self.auto_attach)))):
+ no_auto = ("The service-level key must be used in conjunction "
+ "with the auto-attach key. Please re-run with "
+ "auto-attach: True")
+ return False, no_auto
+ return True, None
+
+ def _is_registered(self):
+ '''
+ Checks if the system is already registered and returns
+ True if so, else False
+ '''
+ cmd = ['identity']
+
+ try:
+ self._sub_man_cli(cmd)
+ except util.ProcessExecutionError:
+ return False
+
+ return True
+
+ def _sub_man_cli(self, cmd, logstring_val=False):
+ '''
+ Uses the prefered cloud-init subprocess def of util.subp
+ and runs subscription-manager. Breaking this to a
+ separate function for later use in mocking and unittests
+ '''
+ cmd = self.subman + cmd
+ return util.subp(cmd, logstring=logstring_val)
+
+ def rhn_register(self):
+ '''
+ Registers the system by userid and password or activation key
+ and org. Returns True when successful False when not.
+ '''
+
+ if (self.activation_key is not None) and (self.org is not None):
+ # register by activation key
+ cmd = ['register', '--activationkey={0}'.
+ format(self.activation_key), '--org={0}'.format(self.org)]
+
+ # If the baseurl and/or server url are passed in, we register
+ # with them.
+
+ if self.rhsm_baseurl is not None:
+ cmd.append("--baseurl={0}".format(self.rhsm_baseurl))
+
+ if self.server_hostname is not None:
+ cmd.append("--serverurl={0}".format(self.server_hostname))
+
+ try:
+ return_out, return_err = self._sub_man_cli(cmd,
+ logstring_val=True)
+ except util.ProcessExecutionError as e:
+ if e.stdout == "":
+ self.log_warn("Registration failed due "
+ "to: {0}".format(e.stderr))
+ return False
+
+ elif (self.userid is not None) and (self.password is not None):
+ # register by username and password
+ cmd = ['register', '--username={0}'.format(self.userid),
+ '--password={0}'.format(self.password)]
+
+ # If the baseurl and/or server url are passed in, we register
+ # with them.
+
+ if self.rhsm_baseurl is not None:
+ cmd.append("--baseurl={0}".format(self.rhsm_baseurl))
+
+ if self.server_hostname is not None:
+ cmd.append("--serverurl={0}".format(self.server_hostname))
+
+ # Attempting to register the system only
+ try:
+ return_out, return_err = self._sub_man_cli(cmd,
+ logstring_val=True)
+ except util.ProcessExecutionError as e:
+ if e.stdout == "":
+ self.log_warn("Registration failed due "
+ "to: {0}".format(e.stderr))
+ return False
+
+ else:
+ self.log_warn("Unable to register system due to incomplete "
+ "information.")
+ self.log_warn("Use either activationkey and org *or* userid "
+ "and password")
+ return False
+
+ reg_id = return_out.split("ID: ")[1].rstrip()
+ self.log.debug("Registered successfully with ID {0}".format(reg_id))
+ return True
+
+ def _set_service_level(self):
+ cmd = ['attach', '--auto', '--servicelevel={0}'
+ .format(self.servicelevel)]
+
+ try:
+ return_out, return_err = self._sub_man_cli(cmd)
+ except util.ProcessExecutionError as e:
+ if e.stdout.rstrip() != '':
+ for line in e.stdout.split("\n"):
+ if line is not '':
+ self.log_warn(line)
+ else:
+ self.log_warn("Setting the service level failed with: "
+ "{0}".format(e.stderr.strip()))
+ return False
+ for line in return_out.split("\n"):
+ if line is not "":
+ self.log.debug(line)
+ return True
+
+ def _set_auto_attach(self):
+ cmd = ['attach', '--auto']
+ try:
+ return_out, return_err = self._sub_man_cli(cmd)
+ except util.ProcessExecutionError:
+ self.log_warn("Auto-attach failed with: "
+ "{0}]".format(return_err.strip()))
+ return False
+ for line in return_out.split("\n"):
+ if line is not "":
+ self.log.debug(line)
+ return True
+
+ def _getPools(self):
+ '''
+ Gets the list pools for the active subscription and returns them
+ in list form.
+ '''
+ available = []
+ consumed = []
+
+ # Get all available pools
+ cmd = ['list', '--available', '--pool-only']
+ results, errors = self._sub_man_cli(cmd)
+ available = (results.rstrip()).split("\n")
+
+ # Get all consumed pools
+ cmd = ['list', '--consumed', '--pool-only']
+ results, errors = self._sub_man_cli(cmd)
+ consumed = (results.rstrip()).split("\n")
+
+ return available, consumed
+
+ def _getRepos(self):
+ '''
+ Obtains the current list of active yum repositories and returns
+ them in list form.
+ '''
+
+ cmd = ['repos', '--list-enabled']
+ return_out, return_err = self._sub_man_cli(cmd)
+ active_repos = []
+ for repo in return_out.split("\n"):
+ if "Repo ID:" in repo:
+ active_repos.append((repo.split(':')[1]).strip())
+
+ cmd = ['repos', '--list-disabled']
+ return_out, return_err = self._sub_man_cli(cmd)
+
+ inactive_repos = []
+ for repo in return_out.split("\n"):
+ if "Repo ID:" in repo:
+ inactive_repos.append((repo.split(':')[1]).strip())
+ return active_repos, inactive_repos
+
+ def addPool(self, pools):
+ '''
+ Takes a list of subscription pools and "attaches" them to the
+ current subscription
+ '''
+
+ # An empty list was passed
+ if len(pools) == 0:
+ self.log.debug("No pools to attach")
+ return True
+
+ pool_available, pool_consumed = self._getPools()
+ pool_list = []
+ cmd = ['attach']
+ for pool in pools:
+ if (pool not in pool_consumed) and (pool in pool_available):
+ pool_list.append('--pool={0}'.format(pool))
+ else:
+ self.log_warn("Pool {0} is not available".format(pool))
+ if len(pool_list) > 0:
+ cmd.extend(pool_list)
+ try:
+ self._sub_man_cli(cmd)
+ self.log.debug("Attached the following pools to your "
+ "system: %s" % (", ".join(pool_list))
+ .replace('--pool=', ''))
+ return True
+ except util.ProcessExecutionError as e:
+ self.log_warn("Unable to attach pool {0} "
+ "due to {1}".format(pool, e))
+ return False
+
+ def update_repos(self, erepos, drepos):
+ '''
+ Takes a list of yum repo ids that need to be disabled or enabled; then
+ it verifies if they are already enabled or disabled and finally
+ executes the action to disable or enable
+ '''
+
+ if (erepos is not None) and (not isinstance(erepos, list)):
+ self.log_warn("Repo IDs must in the format of a list.")
+ return False
+
+ if (drepos is not None) and (not isinstance(drepos, list)):
+ self.log_warn("Repo IDs must in the format of a list.")
+ return False
+
+ # Bail if both lists are not populated
+ if (len(erepos) == 0) and (len(drepos) == 0):
+ self.log.debug("No repo IDs to enable or disable")
+ return True
+
+ active_repos, inactive_repos = self._getRepos()
+ # Creating a list of repoids to be enabled
+ enable_list = []
+ enable_list_fail = []
+ for repoid in erepos:
+ if (repoid in inactive_repos):
+ enable_list.append("--enable={0}".format(repoid))
+ else:
+ enable_list_fail.append(repoid)
+
+ # Creating a list of repoids to be disabled
+ disable_list = []
+ disable_list_fail = []
+ for repoid in drepos:
+ if repoid in active_repos:
+ disable_list.append("--disable={0}".format(repoid))
+ else:
+ disable_list_fail.append(repoid)
+
+ # Logging any repos that are already enabled or disabled
+ if len(enable_list_fail) > 0:
+ for fail in enable_list_fail:
+ # Check if the repo exists or not
+ if fail in active_repos:
+ self.log.debug("Repo {0} is already enabled".format(fail))
+ else:
+ self.log_warn("Repo {0} does not appear to "
+ "exist".format(fail))
+ if len(disable_list_fail) > 0:
+ for fail in disable_list_fail:
+ self.log.debug("Repo {0} not disabled "
+ "because it is not enabled".format(fail))
+
+ cmd = ['repos']
+ if len(enable_list) > 0:
+ cmd.extend(enable_list)
+ if len(disable_list) > 0:
+ cmd.extend(disable_list)
+
+ try:
+ self._sub_man_cli(cmd)
+ except util.ProcessExecutionError as e:
+ self.log_warn("Unable to alter repos due to {0}".format(e))
+ return False
+
+ if len(enable_list) > 0:
+ self.log.debug("Enabled the following repos: %s" %
+ (", ".join(enable_list)).replace('--enable=', ''))
+ if len(disable_list) > 0:
+ self.log.debug("Disabled the following repos: %s" %
+ (", ".join(disable_list)).replace('--disable=', ''))
+ return True
diff --git a/cloudinit/config/cc_rightscale_userdata.py b/cloudinit/config/cc_rightscale_userdata.py
index 7d2ec10a..0ecf3a4d 100644
--- a/cloudinit/config/cc_rightscale_userdata.py
+++ b/cloudinit/config/cc_rightscale_userdata.py
@@ -41,7 +41,7 @@ from cloudinit.settings import PER_INSTANCE
from cloudinit import url_helper as uhelp
from cloudinit import util
-from urlparse import parse_qs
+from six.moves.urllib_parse import parse_qs
frequency = PER_INSTANCE
@@ -58,7 +58,7 @@ def handle(name, _cfg, cloud, log, _args):
try:
mdict = parse_qs(ud)
- if mdict or MY_HOOKNAME not in mdict:
+ if not mdict or MY_HOOKNAME not in mdict:
log.debug(("Skipping module %s, "
"did not find %s in parsed"
" raw userdata"), name, MY_HOOKNAME)
@@ -82,7 +82,7 @@ def handle(name, _cfg, cloud, log, _args):
resp = uhelp.readurl(url)
# Ensure its a valid http response (and something gotten)
if resp.ok() and resp.contents:
- util.write_file(fname, str(resp), mode=0700)
+ util.write_file(fname, resp, mode=0o700)
wrote_fns.append(fname)
except Exception as e:
captured_excps.append(e)
diff --git a/cloudinit/config/cc_rsyslog.py b/cloudinit/config/cc_rsyslog.py
index 57486edc..b8642d65 100644
--- a/cloudinit/config/cc_rsyslog.py
+++ b/cloudinit/config/cc_rsyslog.py
@@ -17,37 +17,166 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+rsyslog module allows configuration of syslog logging via rsyslog
+Configuration is done under the cloud-config top level 'rsyslog'.
+
+Under 'rsyslog' you can define:
+ - configs: [default=[]]
+ this is a list. entries in it are a string or a dictionary.
+ each entry has 2 parts:
+ * content
+ * filename
+ if the entry is a string, then it is assigned to 'content'.
+ for each entry, content is written to the provided filename.
+ if filename is not provided, its default is read from 'config_filename'
+
+ Content here can be any valid rsyslog configuration. No format
+ specific format is enforced.
+
+ For simply logging to an existing remote syslog server, via udp:
+ configs: ["*.* @192.168.1.1"]
+
+ - remotes: [default={}]
+ This is a dictionary of name / value pairs.
+ In comparison to 'config's, it is more focused in that it only supports
+ remote syslog configuration. It is not rsyslog specific, and could
+ convert to other syslog implementations.
+
+ Each entry in remotes is a 'name' and a 'value'.
+ * name: an string identifying the entry. good practice would indicate
+ using a consistent and identifiable string for the producer.
+ For example, the MAAS service could use 'maas' as the key.
+ * value consists of the following parts:
+ * optional filter for log messages
+ default if not present: *.*
+ * optional leading '@' or '@@' (indicates udp or tcp respectively).
+ default if not present (udp): @
+ This is rsyslog format for that. if not present, is '@'.
+ * ipv4 or ipv6 or hostname
+ ipv6 addresses must be in [::1] format. (@[fd00::1]:514)
+ * optional port
+ port defaults to 514
+
+ - config_filename: [default=20-cloud-config.conf]
+ this is the file name to use if none is provided in a config entry.
+
+ - config_dir: [default=/etc/rsyslog.d]
+ this directory is used for filenames that are not absolute paths.
+
+ - service_reload_command: [default="auto"]
+ this command is executed if files have been written and thus the syslog
+ daemon needs to be told.
+
+Note, since cloud-init 0.5 a legacy version of rsyslog config has been
+present and is still supported. See below for the mappings between old
+value and new value:
+ old value -> new value
+ 'rsyslog' -> rsyslog/configs
+ 'rsyslog_filename' -> rsyslog/config_filename
+ 'rsyslog_dir' -> rsyslog/config_dir
+
+the legacy config does not support 'service_reload_command'.
+
+Example config:
+ #cloud-config
+ rsyslog:
+ configs:
+ - "*.* @@192.158.1.1"
+ - content: "*.* @@192.0.2.1:10514"
+ filename: 01-example.conf
+ - content: |
+ *.* @@syslogd.example.com
+ remotes:
+ maas: "192.168.1.1"
+ juju: "10.0.4.1"
+ config_dir: config_dir
+ config_filename: config_filename
+ service_reload_command: [your, syslog, restart, command]
+
+Example Legacy config:
+ #cloud-config
+ rsyslog:
+ - "*.* @@192.158.1.1"
+ rsyslog_dir: /etc/rsyslog-config.d/
+ rsyslog_filename: 99-local.conf
+"""
import os
+import re
+import six
+from cloudinit import log as logging
from cloudinit import util
DEF_FILENAME = "20-cloud-config.conf"
DEF_DIR = "/etc/rsyslog.d"
+DEF_RELOAD = "auto"
+DEF_REMOTES = {}
+KEYNAME_CONFIGS = 'configs'
+KEYNAME_FILENAME = 'config_filename'
+KEYNAME_DIR = 'config_dir'
+KEYNAME_RELOAD = 'service_reload_command'
+KEYNAME_LEGACY_FILENAME = 'rsyslog_filename'
+KEYNAME_LEGACY_DIR = 'rsyslog_dir'
+KEYNAME_REMOTES = 'remotes'
-def handle(name, cfg, cloud, log, _args):
- # rsyslog:
- # - "*.* @@192.158.1.1"
- # - content: "*.* @@192.0.2.1:10514"
- # - filename: 01-examplecom.conf
- # content: |
- # *.* @@syslogd.example.com
-
- # process 'rsyslog'
- if 'rsyslog' not in cfg:
- log.debug(("Skipping module named %s,"
- " no 'rsyslog' key in configuration"), name)
- return
+LOG = logging.getLogger(__name__)
- def_dir = cfg.get('rsyslog_dir', DEF_DIR)
- def_fname = cfg.get('rsyslog_filename', DEF_FILENAME)
+COMMENT_RE = re.compile(r'[ ]*[#]+[ ]*')
+HOST_PORT_RE = re.compile(
+ r'^(?P<proto>[@]{0,2})'
+ '(([[](?P<bracket_addr>[^\]]*)[\]])|(?P<addr>[^:]*))'
+ '([:](?P<port>[0-9]+))?$')
+
+def reload_syslog(command=DEF_RELOAD, systemd=False):
+ service = 'rsyslog'
+ if command == DEF_RELOAD:
+ if systemd:
+ cmd = ['systemctl', 'reload-or-try-restart', service]
+ else:
+ cmd = ['service', service, 'restart']
+ else:
+ cmd = command
+ util.subp(cmd, capture=True)
+
+
+def load_config(cfg):
+ # return an updated config with entries of the correct type
+ # support converting the old top level format into new format
+ mycfg = cfg.get('rsyslog', {})
+
+ if isinstance(cfg.get('rsyslog'), list):
+ mycfg = {KEYNAME_CONFIGS: cfg.get('rsyslog')}
+ if KEYNAME_LEGACY_FILENAME in cfg:
+ mycfg[KEYNAME_FILENAME] = cfg[KEYNAME_LEGACY_FILENAME]
+ if KEYNAME_LEGACY_DIR in cfg:
+ mycfg[KEYNAME_DIR] = cfg[KEYNAME_LEGACY_DIR]
+
+ fillup = (
+ (KEYNAME_CONFIGS, [], list),
+ (KEYNAME_DIR, DEF_DIR, six.string_types),
+ (KEYNAME_FILENAME, DEF_FILENAME, six.string_types),
+ (KEYNAME_RELOAD, DEF_RELOAD, six.string_types + (list,)),
+ (KEYNAME_REMOTES, DEF_REMOTES, dict))
+
+ for key, default, vtypes in fillup:
+ if key not in mycfg or not isinstance(mycfg[key], vtypes):
+ mycfg[key] = default
+
+ return mycfg
+
+
+def apply_rsyslog_changes(configs, def_fname, cfg_dir):
+ # apply the changes in 'configs' to the paths in def_fname and cfg_dir
+ # return a list of the files changed
files = []
- for i, ent in enumerate(cfg['rsyslog']):
+ for cur_pos, ent in enumerate(configs):
if isinstance(ent, dict):
if "content" not in ent:
- log.warn("No 'content' entry in config entry %s", i + 1)
+ LOG.warn("No 'content' entry in config entry %s", cur_pos + 1)
continue
content = ent['content']
filename = ent.get("filename", def_fname)
@@ -57,11 +186,10 @@ def handle(name, cfg, cloud, log, _args):
filename = filename.strip()
if not filename:
- log.warn("Entry %s has an empty filename", i + 1)
+ LOG.warn("Entry %s has an empty filename", cur_pos + 1)
continue
- if not filename.startswith("/"):
- filename = os.path.join(def_dir, filename)
+ filename = os.path.join(cfg_dir, filename)
# Truncate filename first time you see it
omode = "ab"
@@ -70,27 +198,164 @@ def handle(name, cfg, cloud, log, _args):
files.append(filename)
try:
- contents = "%s\n" % (content)
- util.write_file(filename, contents, omode=omode)
+ endl = ""
+ if not content.endswith("\n"):
+ endl = "\n"
+ util.write_file(filename, content + endl, omode=omode)
except Exception:
- util.logexc(log, "Failed to write to %s", filename)
+ util.logexc(LOG, "Failed to write to %s", filename)
+
+ return files
+
+
+def parse_remotes_line(line, name=None):
+ try:
+ data, comment = COMMENT_RE.split(line)
+ comment = comment.strip()
+ except ValueError:
+ data, comment = (line, None)
+
+ toks = data.strip().split()
+ match = None
+ if len(toks) == 1:
+ host_port = data
+ elif len(toks) == 2:
+ match, host_port = toks
+ else:
+ raise ValueError("line had multiple spaces: %s" % data)
+
+ toks = HOST_PORT_RE.match(host_port)
+
+ if not toks:
+ raise ValueError("Invalid host specification '%s'" % host_port)
+
+ proto = toks.group('proto')
+ addr = toks.group('addr') or toks.group('bracket_addr')
+ port = toks.group('port')
+
+ if addr.startswith("[") and not addr.endswith("]"):
+ raise ValueError("host spec had invalid brackets: %s" % addr)
+
+ if comment and not name:
+ name = comment
+
+ t = SyslogRemotesLine(name=name, match=match, proto=proto,
+ addr=addr, port=port)
+ t.validate()
+ return t
+
+
+class SyslogRemotesLine(object):
+ def __init__(self, name=None, match=None, proto=None, addr=None,
+ port=None):
+ if not match:
+ match = "*.*"
+ self.name = name
+ self.match = match
+ if not proto:
+ proto = "udp"
+ if proto == "@":
+ proto = "udp"
+ elif proto == "@@":
+ proto = "tcp"
+ self.proto = proto
+
+ self.addr = addr
+ if port:
+ self.port = int(port)
+ else:
+ self.port = None
+
+ def validate(self):
+ if self.port:
+ try:
+ int(self.port)
+ except ValueError:
+ raise ValueError("port '%s' is not an integer" % self.port)
+
+ if not self.addr:
+ raise ValueError("address is required")
+
+ def __repr__(self):
+ return "[name=%s match=%s proto=%s address=%s port=%s]" % (
+ self.name, self.match, self.proto, self.addr, self.port
+ )
+
+ def __str__(self):
+ buf = self.match + " "
+ if self.proto == "udp":
+ buf += "@"
+ elif self.proto == "tcp":
+ buf += "@@"
+
+ if ":" in self.addr:
+ buf += "[" + self.addr + "]"
+ else:
+ buf += self.addr
+
+ if self.port:
+ buf += ":%s" % self.port
+
+ if self.name:
+ buf += " # %s" % self.name
+ return buf
+
+
+def remotes_to_rsyslog_cfg(remotes, header=None, footer=None):
+ if not remotes:
+ return None
+ lines = []
+ if header is not None:
+ lines.append(header)
+ for name, line in remotes.items():
+ if not line:
+ continue
+ try:
+ lines.append(str(parse_remotes_line(line, name=name)))
+ except ValueError as e:
+ LOG.warn("failed loading remote %s: %s [%s]", name, line, e)
+ if footer is not None:
+ lines.append(footer)
+ return '\n'.join(lines) + "\n"
+
+
+def handle(name, cfg, cloud, log, _args):
+ if 'rsyslog' not in cfg:
+ log.debug(("Skipping module named %s,"
+ " no 'rsyslog' key in configuration"), name)
+ return
+
+ mycfg = load_config(cfg)
+ configs = mycfg[KEYNAME_CONFIGS]
+
+ if mycfg[KEYNAME_REMOTES]:
+ configs.append(
+ remotes_to_rsyslog_cfg(
+ mycfg[KEYNAME_REMOTES],
+ header="# begin remotes",
+ footer="# end remotes",
+ ))
+
+ if not mycfg['configs']:
+ log.debug("Empty config rsyslog['configs'], nothing to do")
+ return
+
+ changes = apply_rsyslog_changes(
+ configs=mycfg[KEYNAME_CONFIGS],
+ def_fname=mycfg[KEYNAME_FILENAME],
+ cfg_dir=mycfg[KEYNAME_DIR])
+
+ if not changes:
+ log.debug("restart of syslog not necessary, no changes made")
+ return
- # Attempt to restart syslogd
- restarted = False
try:
- # If this config module is running at cloud-init time
- # (before rsyslog is running) we don't actually have to
- # restart syslog.
- #
- # Upstart actually does what we want here, in that it doesn't
- # start a service that wasn't running already on 'restart'
- # it will also return failure on the attempt, so 'restarted'
- # won't get set.
- log.debug("Restarting rsyslog")
- util.subp(['service', 'rsyslog', 'restart'])
- restarted = True
- except Exception:
- util.logexc(log, "Failed restarting rsyslog")
+ restarted = reload_syslog(
+ command=mycfg[KEYNAME_RELOAD],
+ systemd=cloud.distro.uses_systemd()),
+ except util.ProcessExecutionError as e:
+ restarted = False
+ log.warn("Failed to reload syslog", e)
if restarted:
# This only needs to run if we *actually* restarted
@@ -98,4 +363,4 @@ def handle(name, cfg, cloud, log, _args):
cloud.cycle_logging()
# This should now use rsyslog if
# the logging was setup to use it...
- log.debug("%s configured %s files", name, files)
+ log.debug("%s configured %s files", name, changes)
diff --git a/cloudinit/config/cc_runcmd.py b/cloudinit/config/cc_runcmd.py
index 598c3a3e..66dc3363 100644
--- a/cloudinit/config/cc_runcmd.py
+++ b/cloudinit/config/cc_runcmd.py
@@ -33,6 +33,6 @@ def handle(name, cfg, cloud, log, _args):
cmd = cfg["runcmd"]
try:
content = util.shellify(cmd)
- util.write_file(out_fn, content, 0700)
+ util.write_file(out_fn, content, 0o700)
except:
util.logexc(log, "Failed to shellify %s into file %s", cmd, out_fn)
diff --git a/cloudinit/config/cc_salt_minion.py b/cloudinit/config/cc_salt_minion.py
index 53013dcb..f5786a31 100644
--- a/cloudinit/config/cc_salt_minion.py
+++ b/cloudinit/config/cc_salt_minion.py
@@ -47,7 +47,7 @@ def handle(name, cfg, cloud, log, _args):
# ... copy the key pair if specified
if 'public_key' in salt_cfg and 'private_key' in salt_cfg:
pki_dir = salt_cfg.get('pki_dir', '/etc/salt/pki')
- with util.umask(077):
+ with util.umask(0o77):
util.ensure_dir(pki_dir)
pub_name = os.path.join(pki_dir, 'minion.pub')
pem_name = os.path.join(pki_dir, 'minion.pem')
diff --git a/cloudinit/config/cc_seed_random.py b/cloudinit/config/cc_seed_random.py
index 49a6b3e8..1b011216 100644
--- a/cloudinit/config/cc_seed_random.py
+++ b/cloudinit/config/cc_seed_random.py
@@ -21,7 +21,8 @@
import base64
import os
-from StringIO import StringIO
+
+from six import BytesIO
from cloudinit.settings import PER_INSTANCE
from cloudinit import log as logging
@@ -33,13 +34,13 @@ LOG = logging.getLogger(__name__)
def _decode(data, encoding=None):
if not data:
- return ''
+ return b''
if not encoding or encoding.lower() in ['raw']:
- return data
+ return util.encode_text(data)
elif encoding.lower() in ['base64', 'b64']:
return base64.b64decode(data)
elif encoding.lower() in ['gzip', 'gz']:
- return util.decomp_gzip(data, quiet=False)
+ return util.decomp_gzip(data, quiet=False, decode=None)
else:
raise IOError("Unknown random_seed encoding: %s" % (encoding))
@@ -64,9 +65,9 @@ def handle_random_seed_command(command, required, env=None):
def handle(name, cfg, cloud, log, _args):
mycfg = cfg.get('random_seed', {})
seed_path = mycfg.get('file', '/dev/urandom')
- seed_data = mycfg.get('data', '')
+ seed_data = mycfg.get('data', b'')
- seed_buf = StringIO()
+ seed_buf = BytesIO()
if seed_data:
seed_buf.write(_decode(seed_data, encoding=mycfg.get('encoding')))
@@ -74,7 +75,7 @@ def handle(name, cfg, cloud, log, _args):
# openstack meta_data.json
metadata = cloud.datasource.metadata
if metadata and 'random_seed' in metadata:
- seed_buf.write(metadata['random_seed'])
+ seed_buf.write(util.encode_text(metadata['random_seed']))
seed_data = seed_buf.getvalue()
if len(seed_data):
@@ -82,7 +83,7 @@ def handle(name, cfg, cloud, log, _args):
len(seed_data), seed_path)
util.append_file(seed_path, seed_data)
- command = mycfg.get('command', ['pollinate', '-q'])
+ command = mycfg.get('command', None)
req = mycfg.get('command_required', False)
try:
env = os.environ.copy()
diff --git a/cloudinit/config/cc_set_hostname.py b/cloudinit/config/cc_set_hostname.py
index 5d7f4331..f43d8d5a 100644
--- a/cloudinit/config/cc_set_hostname.py
+++ b/cloudinit/config/cc_set_hostname.py
@@ -24,7 +24,7 @@ from cloudinit import util
def handle(name, cfg, cloud, log, _args):
if util.get_cfg_option_bool(cfg, "preserve_hostname", False):
log.debug(("Configuration option 'preserve_hostname' is set,"
- " not setting the hostname in module %s"), name)
+ " not setting the hostname in module %s"), name)
return
(hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud)
diff --git a/cloudinit/config/cc_set_passwords.py b/cloudinit/config/cc_set_passwords.py
index 4ca85e21..58e1b713 100644
--- a/cloudinit/config/cc_set_passwords.py
+++ b/cloudinit/config/cc_set_passwords.py
@@ -28,11 +28,11 @@ from cloudinit import distros as ds
from cloudinit import ssh_util
from cloudinit import util
-from string import letters, digits
+from string import ascii_letters, digits
# We are removing certain 'painful' letters/numbers
-PW_SET = (letters.translate(None, 'loLOI') +
- digits.translate(None, '01'))
+PW_SET = (''.join([x for x in ascii_letters + digits
+ if x not in 'loLOI01']))
def handle(_name, cfg, cloud, log, args):
@@ -45,8 +45,6 @@ def handle(_name, cfg, cloud, log, args):
password = util.get_cfg_option_str(cfg, "password", None)
expire = True
- pw_auth = "no"
- change_pwauth = False
plist = None
if 'chpasswd' in cfg:
@@ -104,11 +102,24 @@ def handle(_name, cfg, cloud, log, args):
change_pwauth = False
pw_auth = None
if 'ssh_pwauth' in cfg:
- change_pwauth = True
if util.is_true(cfg['ssh_pwauth']):
+ change_pwauth = True
pw_auth = 'yes'
- if util.is_false(cfg['ssh_pwauth']):
+ elif util.is_false(cfg['ssh_pwauth']):
+ change_pwauth = True
pw_auth = 'no'
+ elif str(cfg['ssh_pwauth']).lower() == 'unchanged':
+ log.debug('Leaving auth line unchanged')
+ change_pwauth = False
+ elif not str(cfg['ssh_pwauth']).strip():
+ log.debug('Leaving auth line unchanged')
+ change_pwauth = False
+ elif not cfg['ssh_pwauth']:
+ log.debug('Leaving auth line unchanged')
+ change_pwauth = False
+ else:
+ msg = 'Unrecognized value %s for ssh_pwauth' % cfg['ssh_pwauth']
+ util.logexc(log, msg)
if change_pwauth:
replaced_auth = False
diff --git a/cloudinit/config/cc_snappy.py b/cloudinit/config/cc_snappy.py
new file mode 100644
index 00000000..fa9d54a0
--- /dev/null
+++ b/cloudinit/config/cc_snappy.py
@@ -0,0 +1,304 @@
+# vi: ts=4 expandtab
+#
+"""
+snappy modules allows configuration of snappy.
+Example config:
+ #cloud-config
+ snappy:
+ system_snappy: auto
+ ssh_enabled: auto
+ packages: [etcd, pkg2.smoser]
+ config:
+ pkgname:
+ key2: value2
+ pkg2:
+ key1: value1
+ packages_dir: '/writable/user-data/cloud-init/snaps'
+
+ - ssh_enabled:
+ This controls the system's ssh service. The default value is 'auto'.
+ True: enable ssh service
+ False: disable ssh service
+ auto: enable ssh service if either ssh keys have been provided
+ or user has requested password authentication (ssh_pwauth).
+
+ - snap installation and config
+ The above would install 'etcd', and then install 'pkg2.smoser' with a
+ '<config-file>' argument where 'config-file' has 'config-blob' inside it.
+ If 'pkgname' is installed already, then 'snappy config pkgname <file>'
+ will be called where 'file' has 'pkgname-config-blob' as its content.
+
+ Entries in 'config' can be namespaced or non-namespaced for a package.
+ In either case, the config provided to snappy command is non-namespaced.
+ The package name is provided as it appears.
+
+ If 'packages_dir' has files in it that end in '.snap', then they are
+ installed. Given 3 files:
+ <packages_dir>/foo.snap
+ <packages_dir>/foo.config
+ <packages_dir>/bar.snap
+ cloud-init will invoke:
+ snappy install <packages_dir>/foo.snap <packages_dir>/foo.config
+ snappy install <packages_dir>/bar.snap
+
+ Note, that if provided a 'config' entry for 'ubuntu-core', then
+ cloud-init will invoke: snappy config ubuntu-core <config>
+ Allowing you to configure ubuntu-core in this way.
+"""
+
+from cloudinit import log as logging
+from cloudinit import util
+from cloudinit.settings import PER_INSTANCE
+
+import glob
+import tempfile
+import os
+
+LOG = logging.getLogger(__name__)
+
+frequency = PER_INSTANCE
+SNAPPY_CMD = "snappy"
+NAMESPACE_DELIM = '.'
+
+BUILTIN_CFG = {
+ 'packages': [],
+ 'packages_dir': '/writable/user-data/cloud-init/snaps',
+ 'ssh_enabled': "auto",
+ 'system_snappy': "auto",
+ 'config': {},
+}
+
+
+def parse_filename(fname):
+ fname = os.path.basename(fname)
+ fname_noext = fname.rpartition(".")[0]
+ name = fname_noext.partition("_")[0]
+ shortname = name.partition(".")[0]
+ return(name, shortname, fname_noext)
+
+
+def get_fs_package_ops(fspath):
+ if not fspath:
+ return []
+ ops = []
+ for snapfile in sorted(glob.glob(os.path.sep.join([fspath, '*.snap']))):
+ (name, shortname, fname_noext) = parse_filename(snapfile)
+ cfg = None
+ for cand in (fname_noext, name, shortname):
+ fpcand = os.path.sep.join([fspath, cand]) + ".config"
+ if os.path.isfile(fpcand):
+ cfg = fpcand
+ break
+ ops.append(makeop('install', name, config=None,
+ path=snapfile, cfgfile=cfg))
+ return ops
+
+
+def makeop(op, name, config=None, path=None, cfgfile=None):
+ return({'op': op, 'name': name, 'config': config, 'path': path,
+ 'cfgfile': cfgfile})
+
+
+def get_package_config(configs, name):
+ # load the package's config from the configs dict.
+ # prefer full-name entry (config-example.canonical)
+ # over short name entry (config-example)
+ if name in configs:
+ return configs[name]
+ return configs.get(name.partition(NAMESPACE_DELIM)[0])
+
+
+def get_package_ops(packages, configs, installed=None, fspath=None):
+ # get the install an config operations that should be done
+ if installed is None:
+ installed = read_installed_packages()
+ short_installed = [p.partition(NAMESPACE_DELIM)[0] for p in installed]
+
+ if not packages:
+ packages = []
+ if not configs:
+ configs = {}
+
+ ops = []
+ ops += get_fs_package_ops(fspath)
+
+ for name in packages:
+ ops.append(makeop('install', name, get_package_config(configs, name)))
+
+ to_install = [f['name'] for f in ops]
+ short_to_install = [f['name'].partition(NAMESPACE_DELIM)[0] for f in ops]
+
+ for name in configs:
+ if name in to_install:
+ continue
+ shortname = name.partition(NAMESPACE_DELIM)[0]
+ if shortname in short_to_install:
+ continue
+ if name in installed or shortname in short_installed:
+ ops.append(makeop('config', name,
+ config=get_package_config(configs, name)))
+
+ # prefer config entries to filepath entries
+ for op in ops:
+ if op['op'] != 'install' or not op['cfgfile']:
+ continue
+ name = op['name']
+ fromcfg = get_package_config(configs, op['name'])
+ if fromcfg:
+ LOG.debug("preferring configs[%(name)s] over '%(cfgfile)s'", op)
+ op['cfgfile'] = None
+ op['config'] = fromcfg
+
+ return ops
+
+
+def render_snap_op(op, name, path=None, cfgfile=None, config=None):
+ if op not in ('install', 'config'):
+ raise ValueError("cannot render op '%s'" % op)
+
+ shortname = name.partition(NAMESPACE_DELIM)[0]
+ try:
+ cfg_tmpf = None
+ if config is not None:
+ # input to 'snappy config packagename' must have nested data. odd.
+ # config:
+ # packagename:
+ # config
+ # Note, however, we do not touch config files on disk.
+ nested_cfg = {'config': {shortname: config}}
+ (fd, cfg_tmpf) = tempfile.mkstemp()
+ os.write(fd, util.yaml_dumps(nested_cfg).encode())
+ os.close(fd)
+ cfgfile = cfg_tmpf
+
+ cmd = [SNAPPY_CMD, op]
+ if op == 'install':
+ if path:
+ cmd.append("--allow-unauthenticated")
+ cmd.append(path)
+ else:
+ cmd.append(name)
+ if cfgfile:
+ cmd.append(cfgfile)
+ elif op == 'config':
+ cmd += [name, cfgfile]
+
+ util.subp(cmd)
+
+ finally:
+ if cfg_tmpf:
+ os.unlink(cfg_tmpf)
+
+
+def read_installed_packages():
+ ret = []
+ for (name, date, version, dev) in read_pkg_data():
+ if dev:
+ ret.append(NAMESPACE_DELIM.join([name, dev]))
+ else:
+ ret.append(name)
+ return ret
+
+
+def read_pkg_data():
+ out, err = util.subp([SNAPPY_CMD, "list"])
+ pkg_data = []
+ for line in out.splitlines()[1:]:
+ toks = line.split(sep=None, maxsplit=3)
+ if len(toks) == 3:
+ (name, date, version) = toks
+ dev = None
+ else:
+ (name, date, version, dev) = toks
+ pkg_data.append((name, date, version, dev,))
+ return pkg_data
+
+
+def disable_enable_ssh(enabled):
+ LOG.debug("setting enablement of ssh to: %s", enabled)
+ # do something here that would enable or disable
+ not_to_be_run = "/etc/ssh/sshd_not_to_be_run"
+ if enabled:
+ util.del_file(not_to_be_run)
+ # this is an indempotent operation
+ util.subp(["systemctl", "start", "ssh"])
+ else:
+ # this is an indempotent operation
+ util.subp(["systemctl", "stop", "ssh"])
+ util.write_file(not_to_be_run, "cloud-init\n")
+
+
+def system_is_snappy():
+ # channel.ini is configparser loadable.
+ # snappy will move to using /etc/system-image/config.d/*.ini
+ # this is certainly not a perfect test, but good enough for now.
+ content = util.load_file("/etc/system-image/channel.ini", quiet=True)
+ if 'ubuntu-core' in content.lower():
+ return True
+ if os.path.isdir("/etc/system-image/config.d/"):
+ return True
+ return False
+
+
+def set_snappy_command():
+ global SNAPPY_CMD
+ if util.which("snappy-go"):
+ SNAPPY_CMD = "snappy-go"
+ else:
+ SNAPPY_CMD = "snappy"
+ LOG.debug("snappy command is '%s'", SNAPPY_CMD)
+
+
+def handle(name, cfg, cloud, log, args):
+ cfgin = cfg.get('snappy')
+ if not cfgin:
+ cfgin = {}
+ mycfg = util.mergemanydict([cfgin, BUILTIN_CFG])
+
+ sys_snappy = str(mycfg.get("system_snappy", "auto"))
+ if util.is_false(sys_snappy):
+ LOG.debug("%s: System is not snappy. disabling", name)
+ return
+
+ if sys_snappy.lower() == "auto" and not(system_is_snappy()):
+ LOG.debug("%s: 'auto' mode, and system not snappy", name)
+ return
+
+ set_snappy_command()
+
+ pkg_ops = get_package_ops(packages=mycfg['packages'],
+ configs=mycfg['config'],
+ fspath=mycfg['packages_dir'])
+
+ fails = []
+ for pkg_op in pkg_ops:
+ try:
+ render_snap_op(**pkg_op)
+ except Exception as e:
+ fails.append((pkg_op, e,))
+ LOG.warn("'%s' failed for '%s': %s",
+ pkg_op['op'], pkg_op['name'], e)
+
+ # Default to disabling SSH
+ ssh_enabled = mycfg.get('ssh_enabled', "auto")
+
+ # If the user has not explicitly enabled or disabled SSH, then enable it
+ # when password SSH authentication is requested or there are SSH keys
+ if ssh_enabled == "auto":
+ user_ssh_keys = cloud.get_public_ssh_keys() or None
+ password_auth_enabled = cfg.get('ssh_pwauth', False)
+ if user_ssh_keys:
+ LOG.debug("Enabling SSH, ssh keys found in datasource")
+ ssh_enabled = True
+ elif cfg.get('ssh_authorized_keys'):
+ LOG.debug("Enabling SSH, ssh keys found in config")
+ elif password_auth_enabled:
+ LOG.debug("Enabling SSH, password authentication requested")
+ ssh_enabled = True
+ elif ssh_enabled not in (True, False):
+ LOG.warn("Unknown value '%s' in ssh_enabled", ssh_enabled)
+
+ disable_enable_ssh(ssh_enabled)
+
+ if fails:
+ raise Exception("failed to install/configure snaps")
diff --git a/cloudinit/config/cc_ssh.py b/cloudinit/config/cc_ssh.py
index 4c76581c..d24e43c0 100644
--- a/cloudinit/config/cc_ssh.py
+++ b/cloudinit/config/cc_ssh.py
@@ -20,6 +20,7 @@
import glob
import os
+import sys
# Ensure this is aliased to a name not 'distros'
# since the module attribute 'distros'
@@ -29,30 +30,23 @@ from cloudinit import distros as ds
from cloudinit import ssh_util
from cloudinit import util
-DISABLE_ROOT_OPTS = ("no-port-forwarding,no-agent-forwarding,"
-"no-X11-forwarding,command=\"echo \'Please login as the user \\\"$USER\\\" "
-"rather than the user \\\"root\\\".\';echo;sleep 10\"")
-
-KEY_2_FILE = {
- "rsa_private": ("/etc/ssh/ssh_host_rsa_key", 0600),
- "rsa_public": ("/etc/ssh/ssh_host_rsa_key.pub", 0644),
- "dsa_private": ("/etc/ssh/ssh_host_dsa_key", 0600),
- "dsa_public": ("/etc/ssh/ssh_host_dsa_key.pub", 0644),
- "ecdsa_private": ("/etc/ssh/ssh_host_ecdsa_key", 0600),
- "ecdsa_public": ("/etc/ssh/ssh_host_ecdsa_key.pub", 0644),
-}
-
-PRIV_2_PUB = {
- 'rsa_private': 'rsa_public',
- 'dsa_private': 'dsa_public',
- 'ecdsa_private': 'ecdsa_public',
-}
+DISABLE_ROOT_OPTS = (
+ "no-port-forwarding,no-agent-forwarding,"
+ "no-X11-forwarding,command=\"echo \'Please login as the user \\\"$USER\\\""
+ " rather than the user \\\"root\\\".\';echo;sleep 10\"")
-KEY_GEN_TPL = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"'
+GENERATE_KEY_NAMES = ['rsa', 'dsa', 'ecdsa', 'ed25519']
+KEY_FILE_TPL = '/etc/ssh/ssh_host_%s_key'
-GENERATE_KEY_NAMES = ['rsa', 'dsa', 'ecdsa']
+CONFIG_KEY_TO_FILE = {}
+PRIV_TO_PUB = {}
+for k in GENERATE_KEY_NAMES:
+ CONFIG_KEY_TO_FILE.update({"%s_private" % k: (KEY_FILE_TPL % k, 0o600)})
+ CONFIG_KEY_TO_FILE.update(
+ {"%s_public" % k: (KEY_FILE_TPL % k + ".pub", 0o600)})
+ PRIV_TO_PUB["%s_private" % k] = "%s_public" % k
-KEY_FILE_TPL = '/etc/ssh/ssh_host_%s_key'
+KEY_GEN_TPL = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"'
def handle(_name, cfg, cloud, log, _args):
@@ -68,16 +62,16 @@ def handle(_name, cfg, cloud, log, _args):
if "ssh_keys" in cfg:
# if there are keys in cloud-config, use them
- for (key, val) in cfg["ssh_keys"].iteritems():
- if key in KEY_2_FILE:
- tgt_fn = KEY_2_FILE[key][0]
- tgt_perms = KEY_2_FILE[key][1]
+ for (key, val) in cfg["ssh_keys"].items():
+ if key in CONFIG_KEY_TO_FILE:
+ tgt_fn = CONFIG_KEY_TO_FILE[key][0]
+ tgt_perms = CONFIG_KEY_TO_FILE[key][1]
util.write_file(tgt_fn, val, tgt_perms)
- for (priv, pub) in PRIV_2_PUB.iteritems():
+ for (priv, pub) in PRIV_TO_PUB.items():
if pub in cfg['ssh_keys'] or priv not in cfg['ssh_keys']:
continue
- pair = (KEY_2_FILE[priv][0], KEY_2_FILE[pub][0])
+ pair = (CONFIG_KEY_TO_FILE[priv][0], CONFIG_KEY_TO_FILE[pub][0])
cmd = ['sh', '-xc', KEY_GEN_TPL % pair]
try:
# TODO(harlowja): Is this guard needed?
@@ -92,18 +86,28 @@ def handle(_name, cfg, cloud, log, _args):
genkeys = util.get_cfg_option_list(cfg,
'ssh_genkeytypes',
GENERATE_KEY_NAMES)
+ lang_c = os.environ.copy()
+ lang_c['LANG'] = 'C'
for keytype in genkeys:
keyfile = KEY_FILE_TPL % (keytype)
+ if os.path.exists(keyfile):
+ continue
util.ensure_dir(os.path.dirname(keyfile))
- if not os.path.exists(keyfile):
- cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile]
+ cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile]
+
+ # TODO(harlowja): Is this guard needed?
+ with util.SeLinuxGuard("/etc/ssh", recursive=True):
try:
- # TODO(harlowja): Is this guard needed?
- with util.SeLinuxGuard("/etc/ssh", recursive=True):
- util.subp(cmd, capture=False)
- except:
- util.logexc(log, "Failed generating key type %s to "
- "file %s", keytype, keyfile)
+ out, err = util.subp(cmd, capture=True, env=lang_c)
+ sys.stdout.write(util.decode_binary(out))
+ except util.ProcessExecutionError as e:
+ err = util.decode_binary(e.stderr).lower()
+ if (e.exit_code == 1 and
+ err.lower().startswith("unknown key")):
+ log.debug("ssh-keygen: unknown key type '%s'", keytype)
+ else:
+ util.logexc(log, "Failed generating key type %s to "
+ "file %s", keytype, keyfile)
try:
(users, _groups) = ds.normalize_users_groups(cfg, cloud.distro)
diff --git a/cloudinit/config/cc_ssh_authkey_fingerprints.py b/cloudinit/config/cc_ssh_authkey_fingerprints.py
index 51580633..6ce831bc 100644
--- a/cloudinit/config/cc_ssh_authkey_fingerprints.py
+++ b/cloudinit/config/cc_ssh_authkey_fingerprints.py
@@ -32,7 +32,7 @@ from cloudinit import util
def _split_hash(bin_hash):
split_up = []
- for i in xrange(0, len(bin_hash), 2):
+ for i in range(0, len(bin_hash), 2):
split_up.append(bin_hash[i:i + 2])
return split_up
diff --git a/cloudinit/config/cc_update_etc_hosts.py b/cloudinit/config/cc_update_etc_hosts.py
index d3dd1f32..15703efe 100644
--- a/cloudinit/config/cc_update_etc_hosts.py
+++ b/cloudinit/config/cc_update_etc_hosts.py
@@ -41,10 +41,10 @@ def handle(name, cfg, cloud, log, _args):
if not tpl_fn_name:
raise RuntimeError(("No hosts template could be"
" found for distro %s") %
- (cloud.distro.osfamily))
+ (cloud.distro.osfamily))
templater.render_to_file(tpl_fn_name, '/etc/hosts',
- {'hostname': hostname, 'fqdn': fqdn})
+ {'hostname': hostname, 'fqdn': fqdn})
elif manage_hosts == "localhost":
(hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud)
@@ -57,4 +57,4 @@ def handle(name, cfg, cloud, log, _args):
cloud.distro.update_etc_hosts(hostname, fqdn)
else:
log.debug(("Configuration option 'manage_etc_hosts' is not set,"
- " not managing /etc/hosts in module %s"), name)
+ " not managing /etc/hosts in module %s"), name)
diff --git a/cloudinit/config/cc_update_hostname.py b/cloudinit/config/cc_update_hostname.py
index e396ba13..5b78afe1 100644
--- a/cloudinit/config/cc_update_hostname.py
+++ b/cloudinit/config/cc_update_hostname.py
@@ -29,7 +29,7 @@ frequency = PER_ALWAYS
def handle(name, cfg, cloud, log, _args):
if util.get_cfg_option_bool(cfg, "preserve_hostname", False):
log.debug(("Configuration option 'preserve_hostname' is set,"
- " not updating the hostname in module %s"), name)
+ " not updating the hostname in module %s"), name)
return
(hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud)
diff --git a/cloudinit/config/cc_write_files.py b/cloudinit/config/cc_write_files.py
index a73d6f4e..4b03ea91 100644
--- a/cloudinit/config/cc_write_files.py
+++ b/cloudinit/config/cc_write_files.py
@@ -18,6 +18,7 @@
import base64
import os
+import six
from cloudinit.settings import PER_INSTANCE
from cloudinit import util
@@ -25,7 +26,7 @@ from cloudinit import util
frequency = PER_INSTANCE
DEFAULT_OWNER = "root:root"
-DEFAULT_PERMS = 0644
+DEFAULT_PERMS = 0o644
UNKNOWN_ENC = 'text/plain'
@@ -79,7 +80,7 @@ def write_files(name, files, log):
def decode_perms(perm, default, log):
try:
- if isinstance(perm, (int, long, float)):
+ if isinstance(perm, six.integer_types + (float,)):
# Just 'downcast' it (if a float)
return int(perm)
else:
diff --git a/cloudinit/config/cc_yum_add_repo.py b/cloudinit/config/cc_yum_add_repo.py
index 0d836f28..64fba869 100644
--- a/cloudinit/config/cc_yum_add_repo.py
+++ b/cloudinit/config/cc_yum_add_repo.py
@@ -18,9 +18,10 @@
import os
-from cloudinit import util
-
import configobj
+import six
+
+from cloudinit import util
def _canonicalize_id(repo_id):
@@ -37,7 +38,7 @@ def _format_repo_value(val):
# Can handle 'lists' in certain cases
# See: http://bit.ly/Qqrf1t
return "\n ".join([_format_repo_value(v) for v in val])
- if not isinstance(val, (basestring, str)):
+ if not isinstance(val, six.string_types):
return str(val)
return val
@@ -91,7 +92,7 @@ def handle(name, cfg, _cloud, log, _args):
for req_field in ['baseurl']:
if req_field not in repo_config:
log.warn(("Repository %s does not contain a %s"
- " configuration 'required' entry"),
+ " configuration 'required' entry"),
repo_id, req_field)
missing_required += 1
if not missing_required:
diff --git a/cloudinit/cs_utils.py b/cloudinit/cs_utils.py
index dcf56431..83ac1a0e 100644
--- a/cloudinit/cs_utils.py
+++ b/cloudinit/cs_utils.py
@@ -83,8 +83,8 @@ class CepkoResult(object):
connection = serial.Serial(port=SERIAL_PORT,
timeout=READ_TIMEOUT,
writeTimeout=WRITE_TIMEOUT)
- connection.write(self.request)
- return connection.readline().strip('\x04\n')
+ connection.write(self.request.encode('ascii'))
+ return connection.readline().strip(b'\x04\n').decode('ascii')
def _marshal(self, raw_result):
try:
diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index 5eab780b..418421b9 100644
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -21,12 +21,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+import six
+from six import StringIO
import abc
-import itertools
import os
import re
+import stat
from cloudinit import importer
from cloudinit import log as logging
@@ -36,6 +37,7 @@ from cloudinit import util
from cloudinit.distros.parsers import hosts
+
OSFAMILIES = {
'debian': ['debian', 'ubuntu'],
'redhat': ['fedora', 'rhel'],
@@ -73,6 +75,9 @@ class Distro(object):
# to write this blob out in a distro format
raise NotImplementedError()
+ def _write_network_config(self, settings):
+ raise NotImplementedError()
+
def _find_tz_file(self, tz):
tz_file = os.path.join(self.tz_zone_dir, str(tz))
if not os.path.isfile(tz_file):
@@ -88,6 +93,13 @@ class Distro(object):
self._write_hostname(writeable_hostname, self.hostname_conf_fn)
self._apply_hostname(writeable_hostname)
+ def uses_systemd(self):
+ try:
+ res = os.lstat('/run/systemd/system')
+ return stat.S_ISDIR(res.st_mode)
+ except:
+ return False
+
@abc.abstractmethod
def package_command(self, cmd, args=None, pkgs=None):
raise NotImplementedError()
@@ -108,12 +120,11 @@ class Distro(object):
arch = self.get_primary_arch()
return _get_arch_package_mirror_info(mirror_info, arch)
- def get_package_mirror_info(self, arch=None,
- availability_zone=None):
+ def get_package_mirror_info(self, arch=None, data_source=None):
# This resolves the package_mirrors config option
# down to a single dict of {mirror_name: mirror_url}
arch_info = self._get_arch_package_mirror_info(arch)
- return _get_package_mirror_info(availability_zone=availability_zone,
+ return _get_package_mirror_info(data_source=data_source,
mirror_info=arch_info)
def apply_network(self, settings, bring_up=True):
@@ -124,6 +135,14 @@ class Distro(object):
return self._bring_up_interfaces(dev_names)
return False
+ def apply_network_config(self, netconfig, bring_up=False):
+ # Write it out
+ dev_names = self._write_network_config(netconfig)
+ # Now try to bring them up
+ if bring_up:
+ return self._bring_up_interfaces(dev_names)
+ return False
+
@abc.abstractmethod
def apply_locale(self, locale, out_fn=None):
raise NotImplementedError()
@@ -203,10 +222,19 @@ class Distro(object):
# If the system hostname is different than the previous
# one or the desired one lets update it as well
- if (not sys_hostname) or (sys_hostname == prev_hostname
- and sys_hostname != hostname):
+ if ((not sys_hostname) or (sys_hostname == prev_hostname and
+ sys_hostname != hostname)):
update_files.append(sys_fn)
+ # If something else has changed the hostname after we set it
+ # initially, we should not overwrite those changes (we should
+ # only be setting the hostname once per instance)
+ if (sys_hostname and prev_hostname and
+ sys_hostname != prev_hostname):
+ LOG.info("%s differs from %s, assuming user maintained hostname.",
+ prev_hostname_fn, sys_fn)
+ return
+
# Remove duplicates (incase the previous config filename)
# is the same as the system config filename, don't bother
# doing it twice
@@ -221,11 +249,6 @@ class Distro(object):
util.logexc(LOG, "Failed to write hostname %s to %s", hostname,
fn)
- if (sys_hostname and prev_hostname and
- sys_hostname != prev_hostname):
- LOG.debug("%s differs from %s, assuming user maintained hostname.",
- prev_hostname_fn, sys_fn)
-
# If the system hostname file name was provided set the
# non-fqdn as the transient hostname.
if sys_fn in update_files:
@@ -272,12 +295,12 @@ class Distro(object):
if header:
contents.write("%s\n" % (header))
contents.write("%s\n" % (eh))
- util.write_file(self.hosts_fn, contents.getvalue(), mode=0644)
+ util.write_file(self.hosts_fn, contents.getvalue(), mode=0o644)
def _bring_up_interface(self, device_name):
cmd = ['ifup', device_name]
LOG.debug("Attempting to run bring up interface %s using command %s",
- device_name, cmd)
+ device_name, cmd)
try:
(_out, err) = util.subp(cmd)
if len(err):
@@ -307,6 +330,11 @@ class Distro(object):
LOG.info("User %s already exists, skipping." % name)
return
+ if 'create_groups' in kwargs:
+ create_groups = kwargs.pop('create_groups')
+ else:
+ create_groups = True
+
adduser_cmd = ['useradd', name]
log_adduser_cmd = ['useradd', name]
@@ -317,6 +345,7 @@ class Distro(object):
"gecos": '--comment',
"homedir": '--home',
"primary_group": '--gid',
+ "uid": '--uid',
"groups": '--groups',
"passwd": '--password',
"shell": '--shell',
@@ -333,8 +362,21 @@ class Distro(object):
redact_opts = ['passwd']
+ groups = kwargs.get('groups')
+ if groups:
+ if isinstance(groups, (list, tuple)):
+ kwargs['groups'] = ",".join(groups)
+ else:
+ groups = groups.split(",")
+
+ if create_groups:
+ for group in kwargs.get('groups').split(","):
+ if not util.is_group(group):
+ self.create_group(group)
+ LOG.debug("created group %s for user %s", name, group)
+
# Check the values and create the command
- for key, val in kwargs.iteritems():
+ for key, val in kwargs.items():
if key in adduser_opts and val and isinstance(val, str):
adduser_cmd.extend([adduser_opts[key], val])
@@ -380,6 +422,10 @@ class Distro(object):
if 'plain_text_passwd' in kwargs and kwargs['plain_text_passwd']:
self.set_passwd(name, kwargs['plain_text_passwd'])
+ # Set password if hashed password is provided and non-empty
+ if 'hashed_passwd' in kwargs and kwargs['hashed_passwd']:
+ self.set_passwd(name, kwargs['hashed_passwd'], hashed=True)
+
# Default locking down the account. 'lock_passwd' defaults to True.
# lock account unless lock_password is False.
if kwargs.get('lock_passwd', True):
@@ -393,7 +439,7 @@ class Distro(object):
if 'ssh_authorized_keys' in kwargs:
# Try to handle this in a smart manner.
keys = kwargs['ssh_authorized_keys']
- if isinstance(keys, (basestring, str)):
+ if isinstance(keys, six.string_types):
keys = [keys]
if isinstance(keys, dict):
keys = list(keys.values())
@@ -468,7 +514,7 @@ class Distro(object):
util.make_header(base="added"),
"#includedir %s" % (path), '']
sudoers_contents = "\n".join(lines)
- util.write_file(sudo_base, sudoers_contents, 0440)
+ util.write_file(sudo_base, sudoers_contents, 0o440)
else:
lines = ['', util.make_header(base="added"),
"#includedir %s" % (path), '']
@@ -478,7 +524,7 @@ class Distro(object):
except IOError as e:
util.logexc(LOG, "Failed to write %s", sudo_base)
raise e
- util.ensure_dir(path, 0750)
+ util.ensure_dir(path, 0o750)
def write_sudo_rules(self, user, rules, sudo_file=None):
if not sudo_file:
@@ -491,7 +537,7 @@ class Distro(object):
if isinstance(rules, (list, tuple)):
for rule in rules:
lines.append("%s %s" % (user, rule))
- elif isinstance(rules, (basestring, str)):
+ elif isinstance(rules, six.string_types):
lines.append("%s %s" % (user, rules))
else:
msg = "Can not create sudoers rule addition with type %r"
@@ -506,7 +552,7 @@ class Distro(object):
content,
]
try:
- util.write_file(sudo_file, "\n".join(contents), 0440)
+ util.write_file(sudo_file, "\n".join(contents), 0o440)
except IOError as e:
util.logexc(LOG, "Failed to write sudoers file %s", sudo_file)
raise e
@@ -517,8 +563,10 @@ class Distro(object):
util.logexc(LOG, "Failed to append sudoers file %s", sudo_file)
raise e
- def create_group(self, name, members):
+ def create_group(self, name, members=None):
group_add_cmd = ['groupadd', name]
+ if not members:
+ members = []
# Check if group exists, and then add it doesn't
if util.is_group(name):
@@ -528,21 +576,21 @@ class Distro(object):
util.subp(group_add_cmd)
LOG.info("Created new group %s" % name)
except Exception:
- util.logexc("Failed to create group %s", name)
+ util.logexc(LOG, "Failed to create group %s", name)
# Add members to the group, if so defined
if len(members) > 0:
for member in members:
if not util.is_user(member):
LOG.warn("Unable to add group member '%s' to group '%s'"
- "; user does not exist.", member, name)
+ "; user does not exist.", member, name)
continue
util.subp(['usermod', '-a', '-G', name, member])
LOG.info("Added user '%s' to group '%s'" % (member, name))
-def _get_package_mirror_info(mirror_info, availability_zone=None,
+def _get_package_mirror_info(mirror_info, data_source=None,
mirror_filter=util.search_for_mirror):
# given a arch specific 'mirror_info' entry (from package_mirrors)
# search through the 'search' entries, and fallback appropriately
@@ -550,21 +598,28 @@ def _get_package_mirror_info(mirror_info, availability_zone=None,
if not mirror_info:
mirror_info = {}
- ec2_az_re = ("^[a-z][a-z]-(%s)-[1-9][0-9]*[a-z]$" %
- "north|northeast|east|southeast|south|southwest|west|northwest")
+ # ec2 availability zones are named cc-direction-[0-9][a-d] (us-east-1b)
+ # the region is us-east-1. so region = az[0:-1]
+ directions_re = '|'.join([
+ 'central', 'east', 'north', 'northeast', 'northwest',
+ 'south', 'southeast', 'southwest', 'west'])
+ ec2_az_re = ("^[a-z][a-z]-(%s)-[1-9][0-9]*[a-z]$" % directions_re)
subst = {}
- if availability_zone:
- subst['availability_zone'] = availability_zone
+ if data_source and data_source.availability_zone:
+ subst['availability_zone'] = data_source.availability_zone
+
+ if re.match(ec2_az_re, data_source.availability_zone):
+ subst['ec2_region'] = "%s" % data_source.availability_zone[0:-1]
- if availability_zone and re.match(ec2_az_re, availability_zone):
- subst['ec2_region'] = "%s" % availability_zone[0:-1]
+ if data_source and data_source.region:
+ subst['region'] = data_source.region
results = {}
- for (name, mirror) in mirror_info.get('failsafe', {}).iteritems():
+ for (name, mirror) in mirror_info.get('failsafe', {}).items():
results[name] = mirror
- for (name, searchlist) in mirror_info.get('search', {}).iteritems():
+ for (name, searchlist) in mirror_info.get('search', {}).items():
mirrors = []
for tmpl in searchlist:
try:
@@ -604,30 +659,30 @@ def _get_arch_package_mirror_info(package_mirrors, arch):
# is the standard form used in the rest
# of cloud-init
def _normalize_groups(grp_cfg):
- if isinstance(grp_cfg, (str, basestring)):
+ if isinstance(grp_cfg, six.string_types):
grp_cfg = grp_cfg.strip().split(",")
- if isinstance(grp_cfg, (list)):
+ if isinstance(grp_cfg, list):
c_grp_cfg = {}
for i in grp_cfg:
- if isinstance(i, (dict)):
+ if isinstance(i, dict):
for k, v in i.items():
if k not in c_grp_cfg:
- if isinstance(v, (list)):
+ if isinstance(v, list):
c_grp_cfg[k] = list(v)
- elif isinstance(v, (basestring, str)):
+ elif isinstance(v, six.string_types):
c_grp_cfg[k] = [v]
else:
raise TypeError("Bad group member type %s" %
type_utils.obj_name(v))
else:
- if isinstance(v, (list)):
+ if isinstance(v, list):
c_grp_cfg[k].extend(v)
- elif isinstance(v, (basestring, str)):
+ elif isinstance(v, six.string_types):
c_grp_cfg[k].append(v)
else:
raise TypeError("Bad group member type %s" %
type_utils.obj_name(v))
- elif isinstance(i, (str, basestring)):
+ elif isinstance(i, six.string_types):
if i not in c_grp_cfg:
c_grp_cfg[i] = []
else:
@@ -635,7 +690,7 @@ def _normalize_groups(grp_cfg):
type_utils.obj_name(i))
grp_cfg = c_grp_cfg
groups = {}
- if isinstance(grp_cfg, (dict)):
+ if isinstance(grp_cfg, dict):
for (grp_name, grp_members) in grp_cfg.items():
groups[grp_name] = util.uniq_merge_sorted(grp_members)
else:
@@ -661,29 +716,29 @@ def _normalize_groups(grp_cfg):
# entry 'default' which will be marked as true
# all other users will be marked as false.
def _normalize_users(u_cfg, def_user_cfg=None):
- if isinstance(u_cfg, (dict)):
+ if isinstance(u_cfg, dict):
ad_ucfg = []
for (k, v) in u_cfg.items():
- if isinstance(v, (bool, int, basestring, str, float)):
+ if isinstance(v, (bool, int, float) + six.string_types):
if util.is_true(v):
ad_ucfg.append(str(k))
- elif isinstance(v, (dict)):
+ elif isinstance(v, dict):
v['name'] = k
ad_ucfg.append(v)
else:
raise TypeError(("Unmappable user value type %s"
" for key %s") % (type_utils.obj_name(v), k))
u_cfg = ad_ucfg
- elif isinstance(u_cfg, (str, basestring)):
+ elif isinstance(u_cfg, six.string_types):
u_cfg = util.uniq_merge_sorted(u_cfg)
users = {}
for user_config in u_cfg:
- if isinstance(user_config, (str, basestring, list)):
+ if isinstance(user_config, (list,) + six.string_types):
for u in util.uniq_merge(user_config):
if u and u not in users:
users[u] = {}
- elif isinstance(user_config, (dict)):
+ elif isinstance(user_config, dict):
if 'name' in user_config:
n = user_config.pop('name')
prev_config = users.get(n) or {}
@@ -784,11 +839,11 @@ def normalize_users_groups(cfg, distro):
old_user = cfg['user']
# Translate it into the format that is more useful
# going forward
- if isinstance(old_user, (basestring, str)):
+ if isinstance(old_user, six.string_types):
old_user = {
'name': old_user,
}
- if not isinstance(old_user, (dict)):
+ if not isinstance(old_user, dict):
LOG.warn(("Format for 'user' key must be a string or "
"dictionary and not %s"), type_utils.obj_name(old_user))
old_user = {}
@@ -813,7 +868,7 @@ def normalize_users_groups(cfg, distro):
default_user_config = util.mergemanydict([old_user, distro_user_config])
base_users = cfg.get('users', [])
- if not isinstance(base_users, (list, dict, str, basestring)):
+ if not isinstance(base_users, (list, dict) + six.string_types):
LOG.warn(("Format for 'users' key must be a comma separated string"
" or a dictionary or a list and not %s"),
type_utils.obj_name(base_users))
@@ -822,12 +877,12 @@ def normalize_users_groups(cfg, distro):
if old_user:
# Ensure that when user: is provided that this user
# always gets added (as the default user)
- if isinstance(base_users, (list)):
+ if isinstance(base_users, list):
# Just add it on at the end...
base_users.append({'name': 'default'})
- elif isinstance(base_users, (dict)):
+ elif isinstance(base_users, dict):
base_users['default'] = dict(base_users).get('default', True)
- elif isinstance(base_users, (str, basestring)):
+ elif isinstance(base_users, six.string_types):
# Just append it on to be re-parsed later
base_users += ",default"
@@ -852,11 +907,11 @@ def extract_default(users, default_name=None, default_config=None):
return config['default']
tmp_users = users.items()
- tmp_users = dict(itertools.ifilter(safe_find, tmp_users))
+ tmp_users = dict(filter(safe_find, tmp_users))
if not tmp_users:
return (default_name, default_config)
else:
- name = tmp_users.keys()[0]
+ name = list(tmp_users)[0]
config = tmp_users[name]
config.pop('default', None)
return (name, config)
@@ -866,7 +921,7 @@ def fetch(name):
locs, looked_locs = importer.find_module(name, ['', __name__], ['Distro'])
if not locs:
raise ImportError("No distribution found for distro %s (searched %s)"
- % (name, looked_locs))
+ % (name, looked_locs))
mod = importer.import_module(locs[0])
cls = getattr(mod, 'Distro')
return cls
@@ -877,5 +932,9 @@ def set_etc_timezone(tz, tz_file=None, tz_conf="/etc/timezone",
util.write_file(tz_conf, str(tz).rstrip() + "\n")
# This ensures that the correct tz will be used for the system
if tz_local and tz_file:
- util.copy(tz_file, tz_local)
+ # use a symlink if there exists a symlink or tz_local is not present
+ if os.path.islink(tz_local) or not os.path.exists(tz_local):
+ os.symlink(tz_file, tz_local)
+ else:
+ util.copy(tz_file, tz_local)
return
diff --git a/cloudinit/distros/arch.py b/cloudinit/distros/arch.py
index 68bf1aab..93a2e008 100644
--- a/cloudinit/distros/arch.py
+++ b/cloudinit/distros/arch.py
@@ -66,7 +66,7 @@ class Distro(distros.Distro):
settings, entries)
dev_names = entries.keys()
# Format for netctl
- for (dev, info) in entries.iteritems():
+ for (dev, info) in entries.items():
nameservers = []
net_fn = self.network_conf_dir + dev
net_cfg = {
@@ -74,7 +74,7 @@ class Distro(distros.Distro):
'Interface': dev,
'IP': info.get('bootproto'),
'Address': "('%s/%s')" % (info.get('address'),
- info.get('netmask')),
+ info.get('netmask')),
'Gateway': info.get('gateway'),
'DNS': str(tuple(info.get('dns-nameservers'))).replace(',', '')
}
@@ -86,7 +86,7 @@ class Distro(distros.Distro):
if nameservers:
util.write_file(self.resolve_conf_fn,
- convert_resolv_conf(nameservers))
+ convert_resolv_conf(nameservers))
return dev_names
@@ -102,7 +102,7 @@ class Distro(distros.Distro):
def _bring_up_interface(self, device_name):
cmd = ['netctl', 'restart', device_name]
LOG.debug("Attempting to run bring up interface %s using command %s",
- device_name, cmd)
+ device_name, cmd)
try:
(_out, err) = util.subp(cmd)
if len(err):
@@ -129,7 +129,7 @@ class Distro(distros.Distro):
if not conf:
conf = HostnameConf('')
conf.set_hostname(your_hostname)
- util.write_file(out_fn, str(conf), 0644)
+ util.write_file(out_fn, conf, 0o644)
def _read_system_hostname(self):
sys_hostname = self._read_hostname(self.hostname_conf_fn)
diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py
index b09eb094..5d7e6cfc 100644
--- a/cloudinit/distros/debian.py
+++ b/cloudinit/distros/debian.py
@@ -26,6 +26,7 @@ from cloudinit import distros
from cloudinit import helpers
from cloudinit import log as logging
from cloudinit import util
+from cloudinit import net
from cloudinit.distros.parsers.hostname import HostnameConf
@@ -45,7 +46,8 @@ APT_GET_WRAPPER = {
class Distro(distros.Distro):
hostname_conf_fn = "/etc/hostname"
locale_conf_fn = "/etc/default/locale"
- network_conf_fn = "/etc/network/interfaces"
+ network_conf_fn = "/etc/network/interfaces.d/50-cloud-init.cfg"
+ links_prefix = "/etc/systemd/network/50-cloud-init-"
def __init__(self, name, cfg, paths):
distros.Distro.__init__(self, name, cfg, paths)
@@ -76,6 +78,15 @@ class Distro(distros.Distro):
util.write_file(self.network_conf_fn, settings)
return ['all']
+ def _write_network_config(self, netconfig):
+ ns = net.parse_net_config_data(netconfig)
+ net.render_network_state(target="/", network_state=ns,
+ eni=self.network_conf_fn,
+ links_prefix=self.links_prefix,
+ netrules=None)
+ util.del_file("/etc/network/interfaces.d/eth0.cfg")
+ return []
+
def _bring_up_interfaces(self, device_names):
use_all = False
for d in device_names:
@@ -97,7 +108,7 @@ class Distro(distros.Distro):
if not conf:
conf = HostnameConf('')
conf.set_hostname(your_hostname)
- util.write_file(out_fn, str(conf), 0644)
+ util.write_file(out_fn, str(conf), 0o644)
def _read_system_hostname(self):
sys_hostname = self._read_hostname(self.hostname_conf_fn)
@@ -159,8 +170,9 @@ class Distro(distros.Distro):
# Allow the output of this to flow outwards (ie not be captured)
util.log_time(logfunc=LOG.debug,
- msg="apt-%s [%s]" % (command, ' '.join(cmd)), func=util.subp,
- args=(cmd,), kwargs={'env': e, 'capture': False})
+ msg="apt-%s [%s]" % (command, ' '.join(cmd)),
+ func=util.subp,
+ args=(cmd,), kwargs={'env': e, 'capture': False})
def update_package_sources(self):
self._runner.run("update-sources", self.package_command,
diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py
index c59a074b..91bf4a4e 100644
--- a/cloudinit/distros/freebsd.py
+++ b/cloudinit/distros/freebsd.py
@@ -17,8 +17,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
-
-from StringIO import StringIO
+import six
+from six import StringIO
import re
@@ -207,8 +207,9 @@ class Distro(distros.Distro):
redact_opts = ['passwd']
- for key, val in kwargs.iteritems():
- if key in adduser_opts and val and isinstance(val, basestring):
+ for key, val in kwargs.items():
+ if (key in adduser_opts and val and
+ isinstance(val, six.string_types)):
adduser_cmd.extend([adduser_opts[key], val])
# Redact certain fields from the logs
@@ -287,7 +288,7 @@ class Distro(distros.Distro):
nameservers = []
searchdomains = []
dev_names = entries.keys()
- for (device, info) in entries.iteritems():
+ for (device, info) in entries.items():
# Skip the loopback interface.
if device.startswith('lo'):
continue
@@ -339,7 +340,7 @@ class Distro(distros.Distro):
resolvconf.add_search_domain(domain)
except ValueError:
util.logexc(LOG, "Failed to add search domain %s", domain)
- util.write_file(self.resolv_conf_fn, str(resolvconf), 0644)
+ util.write_file(self.resolv_conf_fn, str(resolvconf), 0o644)
return dev_names
diff --git a/cloudinit/distros/gentoo.py b/cloudinit/distros/gentoo.py
index 09dd0d73..6267dd6e 100644
--- a/cloudinit/distros/gentoo.py
+++ b/cloudinit/distros/gentoo.py
@@ -66,7 +66,7 @@ class Distro(distros.Distro):
def _bring_up_interface(self, device_name):
cmd = ['/etc/init.d/net.%s' % device_name, 'restart']
LOG.debug("Attempting to run bring up interface %s using command %s",
- device_name, cmd)
+ device_name, cmd)
try:
(_out, err) = util.subp(cmd)
if len(err):
@@ -88,7 +88,7 @@ class Distro(distros.Distro):
(_out, err) = util.subp(cmd)
if len(err):
LOG.warn("Running %s resulted in stderr output: %s", cmd,
- err)
+ err)
except util.ProcessExecutionError:
util.logexc(LOG, "Running interface command %s failed", cmd)
return False
@@ -108,7 +108,7 @@ class Distro(distros.Distro):
if not conf:
conf = HostnameConf('')
conf.set_hostname(your_hostname)
- util.write_file(out_fn, str(conf), 0644)
+ util.write_file(out_fn, conf, 0o644)
def _read_system_hostname(self):
sys_hostname = self._read_hostname(self.hostname_conf_fn)
diff --git a/cloudinit/distros/net_util.py b/cloudinit/distros/net_util.py
index 8b28e2d1..cadfa6b6 100644
--- a/cloudinit/distros/net_util.py
+++ b/cloudinit/distros/net_util.py
@@ -103,7 +103,7 @@ def translate_network(settings):
consume[cmd] = args
# Check if anything left over to consume
absorb = False
- for (cmd, args) in consume.iteritems():
+ for (cmd, args) in consume.items():
if cmd == 'iface':
absorb = True
if absorb:
diff --git a/cloudinit/distros/parsers/hostname.py b/cloudinit/distros/parsers/hostname.py
index 617b3c36..efb185d4 100644
--- a/cloudinit/distros/parsers/hostname.py
+++ b/cloudinit/distros/parsers/hostname.py
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
from cloudinit.distros.parsers import chop_comment
@@ -84,5 +84,5 @@ class HostnameConf(object):
hostnames_found.add(head)
if len(hostnames_found) > 1:
raise IOError("Multiple hostnames (%s) found!"
- % (hostnames_found))
+ % (hostnames_found))
return entries
diff --git a/cloudinit/distros/parsers/hosts.py b/cloudinit/distros/parsers/hosts.py
index 94c97051..3c5498ee 100644
--- a/cloudinit/distros/parsers/hosts.py
+++ b/cloudinit/distros/parsers/hosts.py
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
from cloudinit.distros.parsers import chop_comment
diff --git a/cloudinit/distros/parsers/resolv_conf.py b/cloudinit/distros/parsers/resolv_conf.py
index 5733c25a..2ed13d9c 100644
--- a/cloudinit/distros/parsers/resolv_conf.py
+++ b/cloudinit/distros/parsers/resolv_conf.py
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+from six import StringIO
from cloudinit import util
@@ -132,7 +132,7 @@ class ResolvConf(object):
# Some hard limit on 256 chars total
raise ValueError(("Adding %r would go beyond the "
"256 maximum search list character limit")
- % (search_domain))
+ % (search_domain))
self._remove_option('search')
self._contents.append(('option', ['search', s_list, '']))
return flat_sds
diff --git a/cloudinit/distros/parsers/sys_conf.py b/cloudinit/distros/parsers/sys_conf.py
index 20ca1871..6157cf32 100644
--- a/cloudinit/distros/parsers/sys_conf.py
+++ b/cloudinit/distros/parsers/sys_conf.py
@@ -16,7 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
+import six
+from six import StringIO
import pipes
import re
@@ -69,15 +70,14 @@ class SysConf(configobj.ConfigObj):
return out_contents.getvalue()
def _quote(self, value, multiline=False):
- if not isinstance(value, (str, basestring)):
+ if not isinstance(value, six.string_types):
raise ValueError('Value "%s" is not a string' % (value))
if len(value) == 0:
return ''
quot_func = None
if value[0] in ['"', "'"] and value[-1] in ['"', "'"]:
if len(value) == 1:
- quot_func = (lambda x:
- self._get_single_quote(x) % x)
+ quot_func = (lambda x: self._get_single_quote(x) % x)
else:
# Quote whitespace if it isn't the start + end of a shell command
if value.strip().startswith("$(") and value.strip().endswith(")"):
@@ -90,10 +90,10 @@ class SysConf(configobj.ConfigObj):
# to use single quotes which won't get expanded...
if re.search(r"[\n\"']", value):
quot_func = (lambda x:
- self._get_triple_quote(x) % x)
+ self._get_triple_quote(x) % x)
else:
quot_func = (lambda x:
- self._get_single_quote(x) % x)
+ self._get_single_quote(x) % x)
else:
quot_func = pipes.quote
if not quot_func:
diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py
index d9588632..812e7002 100644
--- a/cloudinit/distros/rhel.py
+++ b/cloudinit/distros/rhel.py
@@ -73,7 +73,7 @@ class Distro(distros.Distro):
searchservers = []
dev_names = entries.keys()
use_ipv6 = False
- for (dev, info) in entries.iteritems():
+ for (dev, info) in entries.items():
net_fn = self.network_script_tpl % (dev)
net_cfg = {
'DEVICE': dev,
@@ -111,13 +111,6 @@ class Distro(distros.Distro):
rhel_util.update_sysconfig_file(self.network_conf_fn, net_cfg)
return dev_names
- def uses_systemd(self):
- # Fedora 18 and RHEL 7 were the first adopters in their series
- (dist, vers) = util.system_info()['dist'][:2]
- major = (int)(vers.split('.')[0])
- return ((dist.startswith('Red Hat Enterprise Linux') and major >= 7)
- or (dist.startswith('Fedora') and major >= 18))
-
def apply_locale(self, locale, out_fn=None):
if self.uses_systemd():
if not out_fn:
@@ -132,7 +125,11 @@ class Distro(distros.Distro):
rhel_util.update_sysconfig_file(out_fn, locale_cfg)
def _write_hostname(self, hostname, out_fn):
- if self.uses_systemd():
+ # systemd will never update previous-hostname for us, so
+ # we need to do it ourselves
+ if self.uses_systemd() and out_fn.endswith('/previous-hostname'):
+ util.write_file(out_fn, hostname)
+ elif self.uses_systemd():
util.subp(['hostnamectl', 'set-hostname', str(hostname)])
else:
host_cfg = {
@@ -155,7 +152,9 @@ class Distro(distros.Distro):
return (host_fn, self._read_hostname(host_fn))
def _read_hostname(self, filename, default=None):
- if self.uses_systemd():
+ if self.uses_systemd() and filename.endswith('/previous-hostname'):
+ return util.load_file(filename).strip()
+ elif self.uses_systemd():
(out, _err) = util.subp(['hostname'])
if len(out):
return out
diff --git a/cloudinit/distros/rhel_util.py b/cloudinit/distros/rhel_util.py
index 063d536e..84aad623 100644
--- a/cloudinit/distros/rhel_util.py
+++ b/cloudinit/distros/rhel_util.py
@@ -50,7 +50,7 @@ def update_sysconfig_file(fn, adjustments, allow_empty=False):
]
if not exists:
lines.insert(0, util.make_header())
- util.write_file(fn, "\n".join(lines) + "\n", 0644)
+ util.write_file(fn, "\n".join(lines) + "\n", 0o644)
# Helper function to read a RHEL/SUSE /etc/sysconfig/* file
@@ -86,4 +86,4 @@ def update_resolve_conf_file(fn, dns_servers, search_servers):
r_conf.add_search_domain(s)
except ValueError:
util.logexc(LOG, "Failed at adding search domain %s", s)
- util.write_file(fn, str(r_conf), 0644)
+ util.write_file(fn, r_conf, 0o644)
diff --git a/cloudinit/distros/sles.py b/cloudinit/distros/sles.py
index 43682a12..620c974c 100644
--- a/cloudinit/distros/sles.py
+++ b/cloudinit/distros/sles.py
@@ -62,7 +62,7 @@ class Distro(distros.Distro):
nameservers = []
searchservers = []
dev_names = entries.keys()
- for (dev, info) in entries.iteritems():
+ for (dev, info) in entries.items():
net_fn = self.network_script_tpl % (dev)
mode = info.get('auto')
if mode and mode.lower() == 'true':
@@ -113,7 +113,7 @@ class Distro(distros.Distro):
if not conf:
conf = HostnameConf('')
conf.set_hostname(hostname)
- util.write_file(out_fn, str(conf), 0644)
+ util.write_file(out_fn, str(conf), 0o644)
def _read_system_hostname(self):
host_fn = self.hostname_conf_fn
diff --git a/cloudinit/ec2_utils.py b/cloudinit/ec2_utils.py
index e69d06ff..37b92a83 100644
--- a/cloudinit/ec2_utils.py
+++ b/cloudinit/ec2_utils.py
@@ -17,7 +17,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import functools
-import httplib
import json
from cloudinit import log as logging
@@ -25,7 +24,7 @@ from cloudinit import url_helper
from cloudinit import util
LOG = logging.getLogger(__name__)
-SKIP_USERDATA_CODES = frozenset([httplib.NOT_FOUND])
+SKIP_USERDATA_CODES = frozenset([url_helper.NOT_FOUND])
class MetadataLeafDecoder(object):
@@ -42,6 +41,10 @@ class MetadataLeafDecoder(object):
def __call__(self, field, blob):
if not blob:
return blob
+ try:
+ blob = util.decode_binary(blob)
+ except UnicodeDecodeError:
+ return blob
if self._maybe_json_object(blob):
try:
# Assume it's json, unless it fails parsing...
@@ -70,6 +73,8 @@ class MetadataMaterializer(object):
def _parse(self, blob):
leaves = {}
children = []
+ blob = util.decode_binary(blob)
+
if not blob:
return (leaves, children)
@@ -118,12 +123,12 @@ class MetadataMaterializer(object):
child_url = url_helper.combine_url(base_url, c)
if not child_url.endswith("/"):
child_url += "/"
- child_blob = str(self._caller(child_url))
+ child_blob = self._caller(child_url)
child_contents[c] = self._materialize(child_blob, child_url)
leaf_contents = {}
for (field, resource) in leaves.items():
leaf_url = url_helper.combine_url(base_url, resource)
- leaf_blob = str(self._caller(leaf_url))
+ leaf_blob = self._caller(leaf_url)
leaf_contents[field] = self._leaf_decoder(field, leaf_blob)
joined = {}
joined.update(child_contents)
@@ -160,7 +165,7 @@ def get_instance_userdata(api_version='latest',
timeout=timeout,
retries=retries,
exception_cb=exception_cb)
- user_data = str(response)
+ user_data = response.contents
except url_helper.UrlError as e:
if e.code not in SKIP_USERDATA_CODES:
util.logexc(LOG, "Failed fetching userdata from url %s", ud_url)
@@ -181,10 +186,13 @@ def get_instance_metadata(api_version='latest',
ssl_details=ssl_details, timeout=timeout,
retries=retries)
+ def mcaller(url):
+ return caller(url).contents
+
try:
response = caller(md_url)
- materializer = MetadataMaterializer(str(response),
- md_url, caller,
+ materializer = MetadataMaterializer(response.contents,
+ md_url, mcaller,
leaf_decoder=leaf_decoder)
md = materializer.materialize()
if not isinstance(md, (dict)):
diff --git a/cloudinit/filters/launch_index.py b/cloudinit/filters/launch_index.py
index 5bebd318..baecdac9 100644
--- a/cloudinit/filters/launch_index.py
+++ b/cloudinit/filters/launch_index.py
@@ -61,7 +61,7 @@ class Filter(object):
discarded += 1
LOG.debug(("Discarding %s multipart messages "
"which do not match launch index %s"),
- discarded, self.wanted_idx)
+ discarded, self.wanted_idx)
new_message = copy.copy(message)
new_message.set_payload(new_msgs)
new_message[ud.ATTACHMENT_FIELD] = str(len(new_msgs))
diff --git a/cloudinit/handlers/__init__.py b/cloudinit/handlers/__init__.py
index 059d7495..53d5604a 100644
--- a/cloudinit/handlers/__init__.py
+++ b/cloudinit/handlers/__init__.py
@@ -22,6 +22,7 @@
import abc
import os
+import six
from cloudinit.settings import (PER_ALWAYS, PER_INSTANCE, FREQUENCIES)
@@ -147,7 +148,7 @@ def walker_handle_handler(pdata, _ctype, _filename, payload):
if not modfname.endswith(".py"):
modfname = "%s.py" % (modfname)
# TODO(harlowja): Check if path exists??
- util.write_file(modfname, payload, 0600)
+ util.write_file(modfname, payload, 0o600)
handlers = pdata['handlers']
try:
mod = fixup_handler(importer.import_module(modname))
@@ -162,26 +163,38 @@ def walker_handle_handler(pdata, _ctype, _filename, payload):
def _extract_first_or_bytes(blob, size):
- # Extract the first line upto X bytes or X bytes from more than the
- # first line if the first line does not contain enough bytes
- first_line = blob.split("\n", 1)[0]
- if len(first_line) >= size:
- start = first_line[:size]
- else:
+ # Extract the first line or upto X symbols for text objects
+ # Extract first X bytes for binary objects
+ try:
+ if isinstance(blob, six.string_types):
+ start = blob.split("\n", 1)[0]
+ else:
+ # We want to avoid decoding the whole blob (it might be huge)
+ # By taking 4*size bytes we guarantee to decode size utf8 chars
+ start = blob[:4 * size].decode(errors='ignore').split("\n", 1)[0]
+ if len(start) >= size:
+ start = start[:size]
+ except UnicodeDecodeError:
+ # Bytes array doesn't contain text so return chunk of raw bytes
start = blob[0:size]
return start
def _escape_string(text):
try:
- return text.encode("string-escape")
- except TypeError:
+ return text.encode("string_escape")
+ except (LookupError, TypeError):
try:
- # Unicode doesn't support string-escape...
- return text.encode('unicode-escape')
+ # Unicode (and Python 3's str) doesn't support string_escape...
+ return text.encode('unicode_escape')
except TypeError:
# Give up...
pass
+ except AttributeError:
+ # We're in Python3 and received blob as text
+ # No escaping is needed because bytes are printed
+ # as 'b\xAA\xBB' automatically in Python3
+ pass
return text
@@ -232,7 +245,8 @@ def walk(msg, callback, data):
headers = dict(part)
LOG.debug(headers)
headers['Content-Type'] = ctype
- callback(data, filename, part.get_payload(decode=True), headers)
+ payload = util.fully_decoded_payload(part)
+ callback(data, filename, payload, headers)
partnum = partnum + 1
@@ -249,7 +263,10 @@ def fixup_handler(mod, def_freq=PER_INSTANCE):
def type_from_starts_with(payload, default=None):
- payload_lc = payload.lower()
+ try:
+ payload_lc = util.decode_binary(payload).lower()
+ except UnicodeDecodeError:
+ return default
payload_lc = payload_lc.lstrip()
for text in INCLUSION_SRCH:
if payload_lc.startswith(text):
diff --git a/cloudinit/handlers/boot_hook.py b/cloudinit/handlers/boot_hook.py
index 3a50cf87..a4ea47ac 100644
--- a/cloudinit/handlers/boot_hook.py
+++ b/cloudinit/handlers/boot_hook.py
@@ -50,7 +50,7 @@ class BootHookPartHandler(handlers.Handler):
filepath = os.path.join(self.boothook_dir, filename)
contents = util.strip_prefix_suffix(util.dos2unix(payload),
prefix=BOOTHOOK_PREFIX)
- util.write_file(filepath, contents.lstrip(), 0700)
+ util.write_file(filepath, contents.lstrip(), 0o700)
return filepath
def handle_part(self, data, ctype, filename, payload, frequency):
diff --git a/cloudinit/handlers/cloud_config.py b/cloudinit/handlers/cloud_config.py
index bf994e33..07b6d0e0 100644
--- a/cloudinit/handlers/cloud_config.py
+++ b/cloudinit/handlers/cloud_config.py
@@ -95,7 +95,7 @@ class CloudConfigPartHandler(handlers.Handler):
lines.append(util.yaml_dumps(self.cloud_buf))
else:
lines = []
- util.write_file(self.cloud_fn, "\n".join(lines), 0600)
+ util.write_file(self.cloud_fn, "\n".join(lines), 0o600)
def _extract_mergers(self, payload, headers):
merge_header_headers = ''
diff --git a/cloudinit/handlers/shell_script.py b/cloudinit/handlers/shell_script.py
index 9755ab05..b5087693 100644
--- a/cloudinit/handlers/shell_script.py
+++ b/cloudinit/handlers/shell_script.py
@@ -52,4 +52,4 @@ class ShellScriptPartHandler(handlers.Handler):
filename = util.clean_filename(filename)
payload = util.dos2unix(payload)
path = os.path.join(self.script_dir, filename)
- util.write_file(path, payload, 0700)
+ util.write_file(path, payload, 0o700)
diff --git a/cloudinit/handlers/upstart_job.py b/cloudinit/handlers/upstart_job.py
index 50d193c4..c5bea711 100644
--- a/cloudinit/handlers/upstart_job.py
+++ b/cloudinit/handlers/upstart_job.py
@@ -65,7 +65,7 @@ class UpstartJobPartHandler(handlers.Handler):
payload = util.dos2unix(payload)
path = os.path.join(self.upstart_dir, filename)
- util.write_file(path, payload, 0644)
+ util.write_file(path, payload, 0o644)
if SUITABLE_UPSTART:
util.subp(["initctl", "reload-configuration"], capture=False)
diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py
index e701126e..0cf982f3 100644
--- a/cloudinit/helpers.py
+++ b/cloudinit/helpers.py
@@ -23,10 +23,11 @@
from time import time
import contextlib
-import io
import os
-from ConfigParser import (NoSectionError, NoOptionError, RawConfigParser)
+import six
+from six.moves.configparser import (
+ NoSectionError, NoOptionError, RawConfigParser)
from cloudinit.settings import (PER_INSTANCE, PER_ALWAYS, PER_ONCE,
CFG_ENV_NAME)
@@ -138,9 +139,10 @@ class FileSemaphores(object):
# but the item had run before we did canon_sem_name.
if cname != name and os.path.exists(self._get_path(name, freq)):
LOG.warn("%s has run without canonicalized name [%s].\n"
- "likely the migrator has not yet run. It will run next boot.\n"
- "run manually with: cloud-init single --name=migrator"
- % (name, cname))
+ "likely the migrator has not yet run. "
+ "It will run next boot.\n"
+ "run manually with: cloud-init single --name=migrator"
+ % (name, cname))
return True
return False
@@ -318,10 +320,7 @@ class ContentHandlers(object):
return self.registered[content_type]
def items(self):
- return self.registered.items()
-
- def iteritems(self):
- return self.registered.iteritems()
+ return list(self.registered.items())
class Paths(object):
@@ -337,19 +336,19 @@ class Paths(object):
template_dir = path_cfgs.get('templates_dir', '/etc/cloud/templates/')
self.template_tpl = os.path.join(template_dir, '%s.tmpl')
self.lookups = {
- "handlers": "handlers",
- "scripts": "scripts",
- "vendor_scripts": "scripts/vendor",
- "sem": "sem",
- "boothooks": "boothooks",
- "userdata_raw": "user-data.txt",
- "userdata": "user-data.txt.i",
- "obj_pkl": "obj.pkl",
- "cloud_config": "cloud-config.txt",
- "vendor_cloud_config": "vendor-cloud-config.txt",
- "data": "data",
- "vendordata_raw": "vendor-data.txt",
- "vendordata": "vendor-data.txt.i",
+ "handlers": "handlers",
+ "scripts": "scripts",
+ "vendor_scripts": "scripts/vendor",
+ "sem": "sem",
+ "boothooks": "boothooks",
+ "userdata_raw": "user-data.txt",
+ "userdata": "user-data.txt.i",
+ "obj_pkl": "obj.pkl",
+ "cloud_config": "cloud-config.txt",
+ "vendor_cloud_config": "vendor-cloud-config.txt",
+ "data": "data",
+ "vendordata_raw": "vendor-data.txt",
+ "vendordata": "vendor-data.txt.i",
}
# Set when a datasource becomes active
self.datasource = ds
@@ -449,7 +448,7 @@ class DefaultingConfigParser(RawConfigParser):
def stringify(self, header=None):
contents = ''
- with io.BytesIO() as outputstream:
+ with six.StringIO() as outputstream:
self.write(outputstream)
outputstream.flush()
contents = outputstream.getvalue()
diff --git a/cloudinit/log.py b/cloudinit/log.py
index 622c946c..3c79b9c9 100644
--- a/cloudinit/log.py
+++ b/cloudinit/log.py
@@ -28,7 +28,8 @@ import collections
import os
import sys
-from StringIO import StringIO
+import six
+from six import StringIO
# Logging levels for easy access
CRITICAL = logging.CRITICAL
@@ -72,13 +73,13 @@ def setupLogging(cfg=None):
log_cfgs = []
log_cfg = cfg.get('logcfg')
- if log_cfg and isinstance(log_cfg, (str, basestring)):
+ if log_cfg and isinstance(log_cfg, six.string_types):
# If there is a 'logcfg' entry in the config,
# respect it, it is the old keyname
log_cfgs.append(str(log_cfg))
elif "log_cfgs" in cfg:
for a_cfg in cfg['log_cfgs']:
- if isinstance(a_cfg, (basestring, str)):
+ if isinstance(a_cfg, six.string_types):
log_cfgs.append(a_cfg)
elif isinstance(a_cfg, (collections.Iterable)):
cfg_str = [str(c) for c in a_cfg]
diff --git a/cloudinit/mergers/__init__.py b/cloudinit/mergers/__init__.py
index 03aa1ee1..e13f55ac 100644
--- a/cloudinit/mergers/__init__.py
+++ b/cloudinit/mergers/__init__.py
@@ -18,6 +18,8 @@
import re
+import six
+
from cloudinit import importer
from cloudinit import log as logging
from cloudinit import type_utils
@@ -95,7 +97,7 @@ def dict_extract_mergers(config):
raw_mergers = config.pop('merge_type', None)
if raw_mergers is None:
return parsed_mergers
- if isinstance(raw_mergers, (str, basestring)):
+ if isinstance(raw_mergers, six.string_types):
return string_extract_mergers(raw_mergers)
for m in raw_mergers:
if isinstance(m, (dict)):
diff --git a/cloudinit/mergers/m_dict.py b/cloudinit/mergers/m_dict.py
index a16141fa..87cf1a72 100644
--- a/cloudinit/mergers/m_dict.py
+++ b/cloudinit/mergers/m_dict.py
@@ -16,6 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
DEF_MERGE_TYPE = 'no_replace'
MERGE_TYPES = ('replace', DEF_MERGE_TYPE,)
@@ -57,7 +59,7 @@ class Merger(object):
return new_v
if isinstance(new_v, (list, tuple)) and self._recurse_array:
return self._merger.merge(old_v, new_v)
- if isinstance(new_v, (basestring)) and self._recurse_str:
+ if isinstance(new_v, six.string_types) and self._recurse_str:
return self._merger.merge(old_v, new_v)
if isinstance(new_v, (dict)) and self._recurse_dict:
return self._merger.merge(old_v, new_v)
diff --git a/cloudinit/mergers/m_list.py b/cloudinit/mergers/m_list.py
index 3b87b0fc..81e5c580 100644
--- a/cloudinit/mergers/m_list.py
+++ b/cloudinit/mergers/m_list.py
@@ -16,6 +16,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
DEF_MERGE_TYPE = 'replace'
MERGE_TYPES = ('append', 'prepend', DEF_MERGE_TYPE, 'no_replace')
@@ -73,7 +75,7 @@ class Merger(object):
return old_v
if isinstance(new_v, (list, tuple)) and self._recurse_array:
return self._merger.merge(old_v, new_v)
- if isinstance(new_v, (str, basestring)) and self._recurse_str:
+ if isinstance(new_v, six.string_types) and self._recurse_str:
return self._merger.merge(old_v, new_v)
if isinstance(new_v, (dict)) and self._recurse_dict:
return self._merger.merge(old_v, new_v)
@@ -82,6 +84,6 @@ class Merger(object):
# Ok now we are replacing same indexes
merged_list.extend(value)
common_len = min(len(merged_list), len(merge_with))
- for i in xrange(0, common_len):
+ for i in range(0, common_len):
merged_list[i] = merge_same_index(merged_list[i], merge_with[i])
return merged_list
diff --git a/cloudinit/mergers/m_str.py b/cloudinit/mergers/m_str.py
index e22ce28a..b00c4bf3 100644
--- a/cloudinit/mergers/m_str.py
+++ b/cloudinit/mergers/m_str.py
@@ -17,6 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import six
+
class Merger(object):
def __init__(self, _merger, opts):
@@ -34,11 +36,11 @@ class Merger(object):
# perform the following action, if appending we will
# merge them together, otherwise we will just return value.
def _on_str(self, value, merge_with):
- if not isinstance(value, (basestring)):
+ if not isinstance(value, six.string_types):
return merge_with
if not self._append:
return merge_with
- if isinstance(value, unicode):
- return value + unicode(merge_with)
+ if isinstance(value, six.text_type):
+ return value + six.text_type(merge_with)
else:
- return value + str(merge_with)
+ return value + six.binary_type(merge_with)
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
new file mode 100644
index 00000000..40929c6e
--- /dev/null
+++ b/cloudinit/net/__init__.py
@@ -0,0 +1,751 @@
+# Copyright (C) 2013-2014 Canonical Ltd.
+#
+# Author: Scott Moser <scott.moser@canonical.com>
+# Author: Blake Rouse <blake.rouse@canonical.com>
+#
+# Curtin is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
+
+import base64
+import errno
+import glob
+import gzip
+import io
+import os
+import re
+import shlex
+
+from cloudinit import log as logging
+from cloudinit import util
+from .udev import generate_udev_rule
+from . import network_state
+
+LOG = logging.getLogger(__name__)
+
+SYS_CLASS_NET = "/sys/class/net/"
+LINKS_FNAME_PREFIX = "etc/systemd/network/50-cloud-init-"
+
+NET_CONFIG_OPTIONS = [
+ "address", "netmask", "broadcast", "network", "metric", "gateway",
+ "pointtopoint", "media", "mtu", "hostname", "leasehours", "leasetime",
+ "vendor", "client", "bootfile", "server", "hwaddr", "provider", "frame",
+ "netnum", "endpoint", "local", "ttl",
+ ]
+
+NET_CONFIG_COMMANDS = [
+ "pre-up", "up", "post-up", "down", "pre-down", "post-down",
+ ]
+
+NET_CONFIG_BRIDGE_OPTIONS = [
+ "bridge_ageing", "bridge_bridgeprio", "bridge_fd", "bridge_gcinit",
+ "bridge_hello", "bridge_maxage", "bridge_maxwait", "bridge_stp",
+ ]
+
+DEFAULT_PRIMARY_INTERFACE = 'eth0'
+
+
+def sys_dev_path(devname, path=""):
+ return SYS_CLASS_NET + devname + "/" + path
+
+
+def read_sys_net(devname, path, translate=None, enoent=None, keyerror=None):
+ try:
+ contents = ""
+ with open(sys_dev_path(devname, path), "r") as fp:
+ contents = fp.read().strip()
+ if translate is None:
+ return contents
+
+ try:
+ return translate.get(contents)
+ except KeyError:
+ LOG.debug("found unexpected value '%s' in '%s/%s'", contents,
+ devname, path)
+ if keyerror is not None:
+ return keyerror
+ raise
+ except OSError as e:
+ if e.errno == errno.ENOENT and enoent is not None:
+ return enoent
+ raise
+
+
+def is_up(devname):
+ # The linux kernel says to consider devices in 'unknown'
+ # operstate as up for the purposes of network configuration. See
+ # Documentation/networking/operstates.txt in the kernel source.
+ translate = {'up': True, 'unknown': True, 'down': False}
+ return read_sys_net(devname, "operstate", enoent=False, keyerror=False,
+ translate=translate)
+
+
+def is_wireless(devname):
+ return os.path.exists(sys_dev_path(devname, "wireless"))
+
+
+def is_connected(devname):
+ # is_connected isn't really as simple as that. 2 is
+ # 'physically connected'. 3 is 'not connected'. but a wlan interface will
+ # always show 3.
+ try:
+ iflink = read_sys_net(devname, "iflink", enoent=False)
+ if iflink == "2":
+ return True
+ if not is_wireless(devname):
+ return False
+ LOG.debug("'%s' is wireless, basing 'connected' on carrier", devname)
+
+ return read_sys_net(devname, "carrier", enoent=False, keyerror=False,
+ translate={'0': False, '1': True})
+
+ except IOError as e:
+ if e.errno == errno.EINVAL:
+ return False
+ raise
+
+
+def is_physical(devname):
+ return os.path.exists(sys_dev_path(devname, "device"))
+
+
+def is_present(devname):
+ return os.path.exists(sys_dev_path(devname))
+
+
+def get_devicelist():
+ return os.listdir(SYS_CLASS_NET)
+
+
+class ParserError(Exception):
+ """Raised when parser has issue parsing the interfaces file."""
+
+
+def parse_deb_config_data(ifaces, contents, src_dir, src_path):
+ """Parses the file contents, placing result into ifaces.
+
+ '_source_path' is added to every dictionary entry to define which file
+ the configration information came from.
+
+ :param ifaces: interface dictionary
+ :param contents: contents of interfaces file
+ :param src_dir: directory interfaces file was located
+ :param src_path: file path the `contents` was read
+ """
+ currif = None
+ for line in contents.splitlines():
+ line = line.strip()
+ if line.startswith('#'):
+ continue
+ split = line.split(' ')
+ option = split[0]
+ if option == "source-directory":
+ parsed_src_dir = split[1]
+ if not parsed_src_dir.startswith("/"):
+ parsed_src_dir = os.path.join(src_dir, parsed_src_dir)
+ for expanded_path in glob.glob(parsed_src_dir):
+ dir_contents = os.listdir(expanded_path)
+ dir_contents = [
+ os.path.join(expanded_path, path)
+ for path in dir_contents
+ if (os.path.isfile(os.path.join(expanded_path, path)) and
+ re.match("^[a-zA-Z0-9_-]+$", path) is not None)
+ ]
+ for entry in dir_contents:
+ with open(entry, "r") as fp:
+ src_data = fp.read().strip()
+ abs_entry = os.path.abspath(entry)
+ parse_deb_config_data(
+ ifaces, src_data,
+ os.path.dirname(abs_entry), abs_entry)
+ elif option == "source":
+ new_src_path = split[1]
+ if not new_src_path.startswith("/"):
+ new_src_path = os.path.join(src_dir, new_src_path)
+ for expanded_path in glob.glob(new_src_path):
+ with open(expanded_path, "r") as fp:
+ src_data = fp.read().strip()
+ abs_path = os.path.abspath(expanded_path)
+ parse_deb_config_data(
+ ifaces, src_data,
+ os.path.dirname(abs_path), abs_path)
+ elif option == "auto":
+ for iface in split[1:]:
+ if iface not in ifaces:
+ ifaces[iface] = {
+ # Include the source path this interface was found in.
+ "_source_path": src_path
+ }
+ ifaces[iface]['auto'] = True
+ elif option == "iface":
+ iface, family, method = split[1:4]
+ if iface not in ifaces:
+ ifaces[iface] = {
+ # Include the source path this interface was found in.
+ "_source_path": src_path
+ }
+ elif 'family' in ifaces[iface]:
+ raise ParserError(
+ "Interface %s can only be defined once. "
+ "Re-defined in '%s'." % (iface, src_path))
+ ifaces[iface]['family'] = family
+ ifaces[iface]['method'] = method
+ currif = iface
+ elif option == "hwaddress":
+ ifaces[currif]['hwaddress'] = split[1]
+ elif option in NET_CONFIG_OPTIONS:
+ ifaces[currif][option] = split[1]
+ elif option in NET_CONFIG_COMMANDS:
+ if option not in ifaces[currif]:
+ ifaces[currif][option] = []
+ ifaces[currif][option].append(' '.join(split[1:]))
+ elif option.startswith('dns-'):
+ if 'dns' not in ifaces[currif]:
+ ifaces[currif]['dns'] = {}
+ if option == 'dns-search':
+ ifaces[currif]['dns']['search'] = []
+ for domain in split[1:]:
+ ifaces[currif]['dns']['search'].append(domain)
+ elif option == 'dns-nameservers':
+ ifaces[currif]['dns']['nameservers'] = []
+ for server in split[1:]:
+ ifaces[currif]['dns']['nameservers'].append(server)
+ elif option.startswith('bridge_'):
+ if 'bridge' not in ifaces[currif]:
+ ifaces[currif]['bridge'] = {}
+ if option in NET_CONFIG_BRIDGE_OPTIONS:
+ bridge_option = option.replace('bridge_', '', 1)
+ ifaces[currif]['bridge'][bridge_option] = split[1]
+ elif option == "bridge_ports":
+ ifaces[currif]['bridge']['ports'] = []
+ for iface in split[1:]:
+ ifaces[currif]['bridge']['ports'].append(iface)
+ elif option == "bridge_hw" and split[1].lower() == "mac":
+ ifaces[currif]['bridge']['mac'] = split[2]
+ elif option == "bridge_pathcost":
+ if 'pathcost' not in ifaces[currif]['bridge']:
+ ifaces[currif]['bridge']['pathcost'] = {}
+ ifaces[currif]['bridge']['pathcost'][split[1]] = split[2]
+ elif option == "bridge_portprio":
+ if 'portprio' not in ifaces[currif]['bridge']:
+ ifaces[currif]['bridge']['portprio'] = {}
+ ifaces[currif]['bridge']['portprio'][split[1]] = split[2]
+ elif option.startswith('bond-'):
+ if 'bond' not in ifaces[currif]:
+ ifaces[currif]['bond'] = {}
+ bond_option = option.replace('bond-', '', 1)
+ ifaces[currif]['bond'][bond_option] = split[1]
+ for iface in ifaces.keys():
+ if 'auto' not in ifaces[iface]:
+ ifaces[iface]['auto'] = False
+
+
+def parse_deb_config(path):
+ """Parses a debian network configuration file."""
+ ifaces = {}
+ with open(path, "r") as fp:
+ contents = fp.read().strip()
+ abs_path = os.path.abspath(path)
+ parse_deb_config_data(
+ ifaces, contents,
+ os.path.dirname(abs_path), abs_path)
+ return ifaces
+
+
+def parse_net_config_data(net_config):
+ """Parses the config, returns NetworkState dictionary
+
+ :param net_config: curtin network config dict
+ """
+ state = None
+ if 'version' in net_config and 'config' in net_config:
+ ns = network_state.NetworkState(version=net_config.get('version'),
+ config=net_config.get('config'))
+ ns.parse_config()
+ state = ns.network_state
+
+ return state
+
+
+def parse_net_config(path):
+ """Parses a curtin network configuration file and
+ return network state"""
+ ns = None
+ net_config = util.read_conf(path)
+ if 'network' in net_config:
+ ns = parse_net_config_data(net_config.get('network'))
+
+ return ns
+
+
+def _load_shell_content(content, add_empty=False, empty_val=None):
+ """Given shell like syntax (key=value\nkey2=value2\n) in content
+ return the data in dictionary form. If 'add_empty' is True
+ then add entries in to the returned dictionary for 'VAR='
+ variables. Set their value to empty_val."""
+ data = {}
+ for line in shlex.split(content):
+ key, value = line.split("=", 1)
+ if not value:
+ value = empty_val
+ if add_empty or value:
+ data[key] = value
+
+ return data
+
+
+def _klibc_to_config_entry(content, mac_addrs=None):
+ """Convert a klibc writtent shell content file to a 'config' entry
+ When ip= is seen on the kernel command line in debian initramfs
+ and networking is brought up, ipconfig will populate
+ /run/net-<name>.cfg.
+
+ The files are shell style syntax, and examples are in the tests
+ provided here. There is no good documentation on this unfortunately.
+
+ DEVICE=<name> is expected/required and PROTO should indicate if
+ this is 'static' or 'dhcp'.
+ """
+
+ if mac_addrs is None:
+ mac_addrs = {}
+
+ data = _load_shell_content(content)
+ try:
+ name = data['DEVICE']
+ except KeyError:
+ raise ValueError("no 'DEVICE' entry in data")
+
+ # ipconfig on precise does not write PROTO
+ proto = data.get('PROTO')
+ if not proto:
+ if data.get('filename'):
+ proto = 'dhcp'
+ else:
+ proto = 'static'
+
+ if proto not in ('static', 'dhcp'):
+ raise ValueError("Unexpected value for PROTO: %s" % proto)
+
+ iface = {
+ 'type': 'physical',
+ 'name': name,
+ 'subnets': [],
+ }
+
+ if name in mac_addrs:
+ iface['mac_address'] = mac_addrs[name]
+
+ # originally believed there might be IPV6* values
+ for v, pre in (('ipv4', 'IPV4'),):
+ # if no IPV4ADDR or IPV6ADDR, then go on.
+ if pre + "ADDR" not in data:
+ continue
+ subnet = {'type': proto}
+
+ # these fields go right on the subnet
+ for key in ('NETMASK', 'BROADCAST', 'GATEWAY'):
+ if pre + key in data:
+ subnet[key.lower()] = data[pre + key]
+
+ dns = []
+ # handle IPV4DNS0 or IPV6DNS0
+ for nskey in ('DNS0', 'DNS1'):
+ ns = data.get(pre + nskey)
+ # verify it has something other than 0.0.0.0 (or ipv6)
+ if ns and len(ns.strip(":.0")):
+ dns.append(data[pre + nskey])
+ if dns:
+ subnet['dns_nameservers'] = dns
+ # add search to both ipv4 and ipv6, as it has no namespace
+ search = data.get('DOMAINSEARCH')
+ if search:
+ if ',' in search:
+ subnet['dns_search'] = search.split(",")
+ else:
+ subnet['dns_search'] = search.split()
+
+ iface['subnets'].append(subnet)
+
+ return name, iface
+
+
+def config_from_klibc_net_cfg(files=None, mac_addrs=None):
+ if files is None:
+ files = glob.glob('/run/net*.conf')
+
+ entries = []
+ names = {}
+ for cfg_file in files:
+ name, entry = _klibc_to_config_entry(util.load_file(cfg_file),
+ mac_addrs=mac_addrs)
+ if name in names:
+ raise ValueError(
+ "device '%s' defined multiple times: %s and %s" % (
+ name, names[name], cfg_file))
+
+ names[name] = cfg_file
+ entries.append(entry)
+ return {'config': entries, 'version': 1}
+
+
+def render_persistent_net(network_state):
+ ''' Given state, emit udev rules to map
+ mac to ifname
+ '''
+ content = ""
+ interfaces = network_state.get('interfaces')
+ for iface in interfaces.values():
+ # for physical interfaces write out a persist net udev rule
+ if iface['type'] == 'physical' and \
+ 'name' in iface and iface.get('mac_address'):
+ content += generate_udev_rule(iface['name'],
+ iface['mac_address'])
+
+ return content
+
+
+# TODO: switch valid_map based on mode inet/inet6
+def iface_add_subnet(iface, subnet):
+ content = ""
+ valid_map = [
+ 'address',
+ 'netmask',
+ 'broadcast',
+ 'metric',
+ 'gateway',
+ 'pointopoint',
+ 'mtu',
+ 'scope',
+ 'dns_search',
+ 'dns_nameservers',
+ ]
+ for key, value in subnet.items():
+ if value and key in valid_map:
+ if type(value) == list:
+ value = " ".join(value)
+ if '_' in key:
+ key = key.replace('_', '-')
+ content += " {} {}\n".format(key, value)
+
+ return content
+
+
+# TODO: switch to valid_map for attrs
+def iface_add_attrs(iface):
+ content = ""
+ ignore_map = [
+ 'type',
+ 'name',
+ 'inet',
+ 'mode',
+ 'index',
+ 'subnets',
+ ]
+ if iface['type'] not in ['bond', 'bridge', 'vlan']:
+ ignore_map.append('mac_address')
+
+ for key, value in iface.items():
+ if value and key not in ignore_map:
+ if type(value) == list:
+ value = " ".join(value)
+ content += " {} {}\n".format(key, value)
+
+ return content
+
+
+def render_route(route, indent=""):
+ """ When rendering routes for an iface, in some cases applying a route
+ may result in the route command returning non-zero which produces
+ some confusing output for users manually using ifup/ifdown[1]. To
+ that end, we will optionally include an '|| true' postfix to each
+ route line allowing users to work with ifup/ifdown without using
+ --force option.
+
+ We may at somepoint not want to emit this additional postfix, and
+ add a 'strict' flag to this function. When called with strict=True,
+ then we will not append the postfix.
+
+ 1. http://askubuntu.com/questions/168033/
+ how-to-set-static-routes-in-ubuntu-server
+ """
+ content = ""
+ up = indent + "post-up route add"
+ down = indent + "pre-down route del"
+ eol = " || true\n"
+ mapping = {
+ 'network': '-net',
+ 'netmask': 'netmask',
+ 'gateway': 'gw',
+ 'metric': 'metric',
+ }
+ if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0':
+ default_gw = " default gw %s" % route['gateway']
+ content += up + default_gw + eol
+ content += down + default_gw + eol
+ elif route['network'] == '::' and route['netmask'] == 0:
+ # ipv6!
+ default_gw = " -A inet6 default gw %s" % route['gateway']
+ content += up + default_gw + eol
+ content += down + default_gw + eol
+ else:
+ route_line = ""
+ for k in ['network', 'netmask', 'gateway', 'metric']:
+ if k in route:
+ route_line += " %s %s" % (mapping[k], route[k])
+ content += up + route_line + eol
+ content += down + route_line + eol
+
+ return content
+
+
+def render_interfaces(network_state):
+ ''' Given state, emit etc/network/interfaces content '''
+
+ content = ""
+ interfaces = network_state.get('interfaces')
+ ''' Apply a sort order to ensure that we write out
+ the physical interfaces first; this is critical for
+ bonding
+ '''
+ order = {
+ 'physical': 0,
+ 'bond': 1,
+ 'bridge': 2,
+ 'vlan': 3,
+ }
+ content += "auto lo\niface lo inet loopback\n"
+ for dnskey, value in network_state.get('dns', {}).items():
+ if len(value):
+ content += " dns-{} {}\n".format(dnskey, " ".join(value))
+
+ content += "\n"
+ for iface in sorted(interfaces.values(),
+ key=lambda k: (order[k['type']], k['name'])):
+ content += "auto {name}\n".format(**iface)
+
+ subnets = iface.get('subnets', {})
+ if subnets:
+ for index, subnet in zip(range(0, len(subnets)), subnets):
+ iface['index'] = index
+ iface['mode'] = subnet['type']
+ if iface['mode'].endswith('6'):
+ iface['inet'] += '6'
+ elif iface['mode'] == 'static' and ":" in subnet['address']:
+ iface['inet'] += '6'
+ if iface['mode'].startswith('dhcp'):
+ iface['mode'] = 'dhcp'
+
+ if index == 0:
+ content += "iface {name} {inet} {mode}\n".format(**iface)
+ else:
+ content += "auto {name}:{index}\n".format(**iface)
+ content += \
+ "iface {name}:{index} {inet} {mode}\n".format(**iface)
+
+ content += iface_add_subnet(iface, subnet)
+ content += iface_add_attrs(iface)
+ for route in subnet.get('routes', []):
+ content += render_route(route, indent=" ")
+ content += "\n"
+ else:
+ content += "iface {name} {inet} {mode}\n".format(**iface)
+ content += iface_add_attrs(iface)
+ content += "\n"
+
+ for route in network_state.get('routes'):
+ content += render_route(route)
+
+ # global replacements until v2 format
+ content = content.replace('mac_address', 'hwaddress ether')
+ return content
+
+
+def render_network_state(target, network_state, eni="etc/network/interfaces",
+ links_prefix=LINKS_FNAME_PREFIX,
+ netrules='etc/udev/rules.d/70-persistent-net.rules'):
+
+ fpeni = os.path.sep.join((target, eni,))
+ util.ensure_dir(os.path.dirname(fpeni))
+ with open(fpeni, 'w+') as f:
+ f.write(render_interfaces(network_state))
+
+ if netrules:
+ netrules = os.path.sep.join((target, netrules,))
+ util.ensure_dir(os.path.dirname(netrules))
+ with open(netrules, 'w+') as f:
+ f.write(render_persistent_net(network_state))
+
+ if links_prefix:
+ render_systemd_links(target, network_state, links_prefix)
+
+
+def render_systemd_links(target, network_state,
+ links_prefix=LINKS_FNAME_PREFIX):
+ fp_prefix = os.path.sep.join((target, links_prefix))
+ for f in glob.glob(fp_prefix + "*"):
+ os.unlink(f)
+
+ interfaces = network_state.get('interfaces')
+ for iface in interfaces.values():
+ if (iface['type'] == 'physical' and 'name' in iface and
+ iface.get('mac_address')):
+ fname = fp_prefix + iface['name'] + ".link"
+ with open(fname, "w") as fp:
+ fp.write("\n".join([
+ "[Match]",
+ "MACAddress=" + iface['mac_address'],
+ "",
+ "[Link]",
+ "Name=" + iface['name'],
+ ""
+ ]))
+
+
+def is_disabled_cfg(cfg):
+ if not cfg or not isinstance(cfg, dict):
+ return False
+ return cfg.get('config') == "disabled"
+
+
+def sys_netdev_info(name, field):
+ if not os.path.exists(os.path.join(SYS_CLASS_NET, name)):
+ raise OSError("%s: interface does not exist in %s" %
+ (name, SYS_CLASS_NET))
+
+ fname = os.path.join(SYS_CLASS_NET, name, field)
+ if not os.path.exists(fname):
+ raise OSError("%s: could not find sysfs entry: %s" % (name, fname))
+ data = util.load_file(fname)
+ if data[-1] == '\n':
+ data = data[:-1]
+ return data
+
+
+def generate_fallback_config():
+ """Determine which attached net dev is most likely to have a connection and
+ generate network state to run dhcp on that interface"""
+ # by default use eth0 as primary interface
+ nconf = {'config': [], 'version': 1}
+
+ # get list of interfaces that could have connections
+ invalid_interfaces = set(['lo'])
+ potential_interfaces = set(get_devicelist())
+ potential_interfaces = potential_interfaces.difference(invalid_interfaces)
+ # sort into interfaces with carrier, interfaces which could have carrier,
+ # and ignore interfaces that are definitely disconnected
+ connected = []
+ possibly_connected = []
+ for interface in potential_interfaces:
+ try:
+ carrier = int(sys_netdev_info(interface, 'carrier'))
+ if carrier:
+ connected.append(interface)
+ continue
+ except OSError:
+ pass
+ # check if nic is dormant or down, as this may make a nick appear to
+ # not have a carrier even though it could acquire one when brought
+ # online by dhclient
+ try:
+ dormant = int(sys_netdev_info(interface, 'dormant'))
+ if dormant:
+ possibly_connected.append(interface)
+ continue
+ except OSError:
+ pass
+ try:
+ operstate = sys_netdev_info(interface, 'operstate')
+ if operstate in ['dormant', 'down', 'lowerlayerdown', 'unknown']:
+ possibly_connected.append(interface)
+ continue
+ except OSError:
+ pass
+
+ # don't bother with interfaces that might not be connected if there are
+ # some that definitely are
+ if connected:
+ potential_interfaces = connected
+ else:
+ potential_interfaces = possibly_connected
+ # if there are no interfaces, give up
+ if not potential_interfaces:
+ return
+ # if eth0 exists use it above anything else, otherwise get the interface
+ # that looks 'first'
+ if DEFAULT_PRIMARY_INTERFACE in potential_interfaces:
+ name = DEFAULT_PRIMARY_INTERFACE
+ else:
+ name = sorted(potential_interfaces)[0]
+
+ mac = sys_netdev_info(name, 'address')
+ target_name = name
+
+ nconf['config'].append(
+ {'type': 'physical', 'name': target_name,
+ 'mac_address': mac, 'subnets': [{'type': 'dhcp'}]})
+ return nconf
+
+
+def _decomp_gzip(blob, strict=True):
+ # decompress blob. raise exception if not compressed unless strict=False.
+ with io.BytesIO(blob) as iobuf:
+ gzfp = None
+ try:
+ gzfp = gzip.GzipFile(mode="rb", fileobj=iobuf)
+ return gzfp.read()
+ except IOError:
+ if strict:
+ raise
+ return blob
+ finally:
+ if gzfp:
+ gzfp.close()
+
+
+def _b64dgz(b64str, gzipped="try"):
+ # decode a base64 string. If gzipped is true, transparently uncompresss
+ # if gzipped is 'try', then try gunzip, returning the original on fail.
+ try:
+ blob = base64.b64decode(b64str)
+ except TypeError:
+ raise ValueError("Invalid base64 text: %s" % b64str)
+
+ if not gzipped:
+ return blob
+
+ return _decomp_gzip(blob, strict=gzipped != "try")
+
+
+def read_kernel_cmdline_config(files=None, mac_addrs=None, cmdline=None):
+ if cmdline is None:
+ cmdline = util.get_cmdline()
+
+ if 'network-config=' in cmdline:
+ data64 = None
+ for tok in cmdline.split():
+ if tok.startswith("network-config="):
+ data64 = tok.split("=", 1)[1]
+ if data64:
+ return util.load_yaml(_b64dgz(data64))
+
+ if 'ip=' not in cmdline:
+ return None
+
+ if mac_addrs is None:
+ mac_addrs = {k: sys_netdev_info(k, 'address')
+ for k in get_devicelist()}
+
+ return config_from_klibc_net_cfg(files=files, mac_addrs=mac_addrs)
+
+
+# vi: ts=4 expandtab syntax=python
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
new file mode 100644
index 00000000..e32d2cdf
--- /dev/null
+++ b/cloudinit/net/network_state.py
@@ -0,0 +1,446 @@
+# Copyright (C) 2013-2014 Canonical Ltd.
+#
+# Author: Ryan Harper <ryan.harper@canonical.com>
+#
+# Curtin is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
+
+from cloudinit import log as logging
+from cloudinit import util
+from cloudinit.util import yaml_dumps as dump_config
+
+LOG = logging.getLogger(__name__)
+
+NETWORK_STATE_VERSION = 1
+NETWORK_STATE_REQUIRED_KEYS = {
+ 1: ['version', 'config', 'network_state'],
+}
+
+
+def from_state_file(state_file):
+ network_state = None
+ state = util.read_conf(state_file)
+ network_state = NetworkState()
+ network_state.load(state)
+
+ return network_state
+
+
+class NetworkState:
+ def __init__(self, version=NETWORK_STATE_VERSION, config=None):
+ self.version = version
+ self.config = config
+ self.network_state = {
+ 'interfaces': {},
+ 'routes': [],
+ 'dns': {
+ 'nameservers': [],
+ 'search': [],
+ }
+ }
+ self.command_handlers = self.get_command_handlers()
+
+ def get_command_handlers(self):
+ METHOD_PREFIX = 'handle_'
+ methods = filter(lambda x: callable(getattr(self, x)) and
+ x.startswith(METHOD_PREFIX), dir(self))
+ handlers = {}
+ for m in methods:
+ key = m.replace(METHOD_PREFIX, '')
+ handlers[key] = getattr(self, m)
+
+ return handlers
+
+ def dump(self):
+ state = {
+ 'version': self.version,
+ 'config': self.config,
+ 'network_state': self.network_state,
+ }
+ return dump_config(state)
+
+ def load(self, state):
+ if 'version' not in state:
+ LOG.error('Invalid state, missing version field')
+ raise Exception('Invalid state, missing version field')
+
+ required_keys = NETWORK_STATE_REQUIRED_KEYS[state['version']]
+ if not self.valid_command(state, required_keys):
+ msg = 'Invalid state, missing keys: {}'.format(required_keys)
+ LOG.error(msg)
+ raise Exception(msg)
+
+ # v1 - direct attr mapping, except version
+ for key in [k for k in required_keys if k not in ['version']]:
+ setattr(self, key, state[key])
+ self.command_handlers = self.get_command_handlers()
+
+ def dump_network_state(self):
+ return dump_config(self.network_state)
+
+ def parse_config(self):
+ # rebuild network state
+ for command in self.config:
+ handler = self.command_handlers.get(command['type'])
+ handler(command)
+
+ def valid_command(self, command, required_keys):
+ if not required_keys:
+ return False
+
+ found_keys = [key for key in command.keys() if key in required_keys]
+ return len(found_keys) == len(required_keys)
+
+ def handle_physical(self, command):
+ '''
+ command = {
+ 'type': 'physical',
+ 'mac_address': 'c0:d6:9f:2c:e8:80',
+ 'name': 'eth0',
+ 'subnets': [
+ {'type': 'dhcp4'}
+ ]
+ }
+ '''
+ required_keys = [
+ 'name',
+ ]
+ if not self.valid_command(command, required_keys):
+ LOG.warn('Skipping Invalid command: {}'.format(command))
+ LOG.debug(self.dump_network_state())
+ return
+
+ interfaces = self.network_state.get('interfaces')
+ iface = interfaces.get(command['name'], {})
+ for param, val in command.get('params', {}).items():
+ iface.update({param: val})
+
+ # convert subnet ipv6 netmask to cidr as needed
+ subnets = command.get('subnets')
+ if subnets:
+ for subnet in subnets:
+ if subnet['type'] == 'static':
+ if 'netmask' in subnet and ':' in subnet['address']:
+ subnet['netmask'] = mask2cidr(subnet['netmask'])
+ for route in subnet.get('routes', []):
+ if 'netmask' in route:
+ route['netmask'] = mask2cidr(route['netmask'])
+ iface.update({
+ 'name': command.get('name'),
+ 'type': command.get('type'),
+ 'mac_address': command.get('mac_address'),
+ 'inet': 'inet',
+ 'mode': 'manual',
+ 'mtu': command.get('mtu'),
+ 'address': None,
+ 'gateway': None,
+ 'subnets': subnets,
+ })
+ self.network_state['interfaces'].update({command.get('name'): iface})
+ self.dump_network_state()
+
+ def handle_vlan(self, command):
+ '''
+ auto eth0.222
+ iface eth0.222 inet static
+ address 10.10.10.1
+ netmask 255.255.255.0
+ hwaddress ether BC:76:4E:06:96:B3
+ vlan-raw-device eth0
+ '''
+ required_keys = [
+ 'name',
+ 'vlan_link',
+ 'vlan_id',
+ ]
+ if not self.valid_command(command, required_keys):
+ print('Skipping Invalid command: {}'.format(command))
+ print(self.dump_network_state())
+ return
+
+ interfaces = self.network_state.get('interfaces')
+ self.handle_physical(command)
+ iface = interfaces.get(command.get('name'), {})
+ iface['vlan-raw-device'] = command.get('vlan_link')
+ iface['vlan_id'] = command.get('vlan_id')
+ interfaces.update({iface['name']: iface})
+
+ def handle_bond(self, command):
+ '''
+ #/etc/network/interfaces
+ auto eth0
+ iface eth0 inet manual
+ bond-master bond0
+ bond-mode 802.3ad
+
+ auto eth1
+ iface eth1 inet manual
+ bond-master bond0
+ bond-mode 802.3ad
+
+ auto bond0
+ iface bond0 inet static
+ address 192.168.0.10
+ gateway 192.168.0.1
+ netmask 255.255.255.0
+ bond-slaves none
+ bond-mode 802.3ad
+ bond-miimon 100
+ bond-downdelay 200
+ bond-updelay 200
+ bond-lacp-rate 4
+ '''
+ required_keys = [
+ 'name',
+ 'bond_interfaces',
+ 'params',
+ ]
+ if not self.valid_command(command, required_keys):
+ print('Skipping Invalid command: {}'.format(command))
+ print(self.dump_network_state())
+ return
+
+ self.handle_physical(command)
+ interfaces = self.network_state.get('interfaces')
+ iface = interfaces.get(command.get('name'), {})
+ for param, val in command.get('params').items():
+ iface.update({param: val})
+ iface.update({'bond-slaves': 'none'})
+ self.network_state['interfaces'].update({iface['name']: iface})
+
+ # handle bond slaves
+ for ifname in command.get('bond_interfaces'):
+ if ifname not in interfaces:
+ cmd = {
+ 'name': ifname,
+ 'type': 'bond',
+ }
+ # inject placeholder
+ self.handle_physical(cmd)
+
+ interfaces = self.network_state.get('interfaces')
+ bond_if = interfaces.get(ifname)
+ bond_if['bond-master'] = command.get('name')
+ # copy in bond config into slave
+ for param, val in command.get('params').items():
+ bond_if.update({param: val})
+ self.network_state['interfaces'].update({ifname: bond_if})
+
+ def handle_bridge(self, command):
+ '''
+ auto br0
+ iface br0 inet static
+ address 10.10.10.1
+ netmask 255.255.255.0
+ bridge_ports eth0 eth1
+ bridge_stp off
+ bridge_fd 0
+ bridge_maxwait 0
+
+ bridge_params = [
+ "bridge_ports",
+ "bridge_ageing",
+ "bridge_bridgeprio",
+ "bridge_fd",
+ "bridge_gcint",
+ "bridge_hello",
+ "bridge_hw",
+ "bridge_maxage",
+ "bridge_maxwait",
+ "bridge_pathcost",
+ "bridge_portprio",
+ "bridge_stp",
+ "bridge_waitport",
+ ]
+ '''
+ required_keys = [
+ 'name',
+ 'bridge_interfaces',
+ 'params',
+ ]
+ if not self.valid_command(command, required_keys):
+ print('Skipping Invalid command: {}'.format(command))
+ print(self.dump_network_state())
+ return
+
+ # find one of the bridge port ifaces to get mac_addr
+ # handle bridge_slaves
+ interfaces = self.network_state.get('interfaces')
+ for ifname in command.get('bridge_interfaces'):
+ if ifname in interfaces:
+ continue
+
+ cmd = {
+ 'name': ifname,
+ }
+ # inject placeholder
+ self.handle_physical(cmd)
+
+ interfaces = self.network_state.get('interfaces')
+ self.handle_physical(command)
+ iface = interfaces.get(command.get('name'), {})
+ iface['bridge_ports'] = command['bridge_interfaces']
+ for param, val in command.get('params').items():
+ iface.update({param: val})
+
+ interfaces.update({iface['name']: iface})
+
+ def handle_nameserver(self, command):
+ required_keys = [
+ 'address',
+ ]
+ if not self.valid_command(command, required_keys):
+ print('Skipping Invalid command: {}'.format(command))
+ print(self.dump_network_state())
+ return
+
+ dns = self.network_state.get('dns')
+ if 'address' in command:
+ addrs = command['address']
+ if not type(addrs) == list:
+ addrs = [addrs]
+ for addr in addrs:
+ dns['nameservers'].append(addr)
+ if 'search' in command:
+ paths = command['search']
+ if not isinstance(paths, list):
+ paths = [paths]
+ for path in paths:
+ dns['search'].append(path)
+
+ def handle_route(self, command):
+ required_keys = [
+ 'destination',
+ ]
+ if not self.valid_command(command, required_keys):
+ print('Skipping Invalid command: {}'.format(command))
+ print(self.dump_network_state())
+ return
+
+ routes = self.network_state.get('routes')
+ network, cidr = command['destination'].split("/")
+ netmask = cidr2mask(int(cidr))
+ route = {
+ 'network': network,
+ 'netmask': netmask,
+ 'gateway': command.get('gateway'),
+ 'metric': command.get('metric'),
+ }
+ routes.append(route)
+
+
+def cidr2mask(cidr):
+ mask = [0, 0, 0, 0]
+ for i in list(range(0, cidr)):
+ idx = int(i / 8)
+ mask[idx] = mask[idx] + (1 << (7 - i % 8))
+ return ".".join([str(x) for x in mask])
+
+
+def ipv4mask2cidr(mask):
+ if '.' not in mask:
+ return mask
+ return sum([bin(int(x)).count('1') for x in mask.split('.')])
+
+
+def ipv6mask2cidr(mask):
+ if ':' not in mask:
+ return mask
+
+ bitCount = [0, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00,
+ 0xff00, 0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc,
+ 0xfffe, 0xffff]
+ cidr = 0
+ for word in mask.split(':'):
+ if not word or int(word, 16) == 0:
+ break
+ cidr += bitCount.index(int(word, 16))
+
+ return cidr
+
+
+def mask2cidr(mask):
+ if ':' in mask:
+ return ipv6mask2cidr(mask)
+ elif '.' in mask:
+ return ipv4mask2cidr(mask)
+ else:
+ return mask
+
+
+if __name__ == '__main__':
+ import sys
+ import random
+ from cloudinit import net
+
+ def load_config(nc):
+ version = nc.get('version')
+ config = nc.get('config')
+ return (version, config)
+
+ def test_parse(network_config):
+ (version, config) = load_config(network_config)
+ ns1 = NetworkState(version=version, config=config)
+ ns1.parse_config()
+ random.shuffle(config)
+ ns2 = NetworkState(version=version, config=config)
+ ns2.parse_config()
+ print("----NS1-----")
+ print(ns1.dump_network_state())
+ print()
+ print("----NS2-----")
+ print(ns2.dump_network_state())
+ print("NS1 == NS2 ?=> {}".format(
+ ns1.network_state == ns2.network_state))
+ eni = net.render_interfaces(ns2.network_state)
+ print(eni)
+ udev_rules = net.render_persistent_net(ns2.network_state)
+ print(udev_rules)
+
+ def test_dump_and_load(network_config):
+ print("Loading network_config into NetworkState")
+ (version, config) = load_config(network_config)
+ ns1 = NetworkState(version=version, config=config)
+ ns1.parse_config()
+ print("Dumping state to file")
+ ns1_dump = ns1.dump()
+ ns1_state = "/tmp/ns1.state"
+ with open(ns1_state, "w+") as f:
+ f.write(ns1_dump)
+
+ print("Loading state from file")
+ ns2 = from_state_file(ns1_state)
+ print("NS1 == NS2 ?=> {}".format(
+ ns1.network_state == ns2.network_state))
+
+ def test_output(network_config):
+ (version, config) = load_config(network_config)
+ ns1 = NetworkState(version=version, config=config)
+ ns1.parse_config()
+ random.shuffle(config)
+ ns2 = NetworkState(version=version, config=config)
+ ns2.parse_config()
+ print("NS1 == NS2 ?=> {}".format(
+ ns1.network_state == ns2.network_state))
+ eni_1 = net.render_interfaces(ns1.network_state)
+ eni_2 = net.render_interfaces(ns2.network_state)
+ print(eni_1)
+ print(eni_2)
+ print("eni_1 == eni_2 ?=> {}".format(
+ eni_1 == eni_2))
+
+ y = util.read_conf(sys.argv[1])
+ network_config = y.get('network')
+ test_parse(network_config)
+ test_dump_and_load(network_config)
+ test_output(network_config)
diff --git a/cloudinit/net/udev.py b/cloudinit/net/udev.py
new file mode 100644
index 00000000..6435ace0
--- /dev/null
+++ b/cloudinit/net/udev.py
@@ -0,0 +1,54 @@
+# Copyright (C) 2015 Canonical Ltd.
+#
+# Author: Ryan Harper <ryan.harper@canonical.com>
+#
+# Curtin is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Curtin. If not, see <http://www.gnu.org/licenses/>.
+
+
+def compose_udev_equality(key, value):
+ """Return a udev comparison clause, like `ACTION=="add"`."""
+ assert key == key.upper()
+ return '%s=="%s"' % (key, value)
+
+
+def compose_udev_attr_equality(attribute, value):
+ """Return a udev attribute comparison clause, like `ATTR{type}=="1"`."""
+ assert attribute == attribute.lower()
+ return 'ATTR{%s}=="%s"' % (attribute, value)
+
+
+def compose_udev_setting(key, value):
+ """Return a udev assignment clause, like `NAME="eth0"`."""
+ assert key == key.upper()
+ return '%s="%s"' % (key, value)
+
+
+def generate_udev_rule(interface, mac):
+ """Return a udev rule to set the name of network interface with `mac`.
+
+ The rule ends up as a single line looking something like:
+
+ SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*",
+ ATTR{address}="ff:ee:dd:cc:bb:aa", NAME="eth0"
+ """
+ rule = ', '.join([
+ compose_udev_equality('SUBSYSTEM', 'net'),
+ compose_udev_equality('ACTION', 'add'),
+ compose_udev_equality('DRIVERS', '?*'),
+ compose_udev_attr_equality('address', mac),
+ compose_udev_setting('NAME', interface),
+ ])
+ return '%s\n' % rule
+
+# vi: ts=4 expandtab syntax=python
diff --git a/cloudinit/netinfo.py b/cloudinit/netinfo.py
index fb40cc0d..e30d6fb5 100644
--- a/cloudinit/netinfo.py
+++ b/cloudinit/netinfo.py
@@ -87,7 +87,7 @@ def netdev_info(empty=""):
devs[curdev][target] = toks[i][len(field) + 1:]
if empty != "":
- for (_devname, dev) in devs.iteritems():
+ for (_devname, dev) in devs.items():
for field in dev:
if dev[field] == "":
dev[field] = empty
@@ -181,7 +181,7 @@ def netdev_pformat():
else:
fields = ['Device', 'Up', 'Address', 'Mask', 'Scope', 'Hw-Address']
tbl = PrettyTable(fields)
- for (dev, d) in netdev.iteritems():
+ for (dev, d) in netdev.items():
tbl.add_row([dev, d["up"], d["addr"], d["mask"], ".", d["hwaddr"]])
if d.get('addr6'):
tbl.add_row([dev, d["up"],
diff --git a/cloudinit/registry.py b/cloudinit/registry.py
new file mode 100644
index 00000000..04368ddf
--- /dev/null
+++ b/cloudinit/registry.py
@@ -0,0 +1,37 @@
+# Copyright 2015 Canonical Ltd.
+# This file is part of cloud-init. See LICENCE file for license information.
+#
+# vi: ts=4 expandtab
+import copy
+
+
+class DictRegistry(object):
+ """A simple registry for a mapping of objects."""
+
+ def __init__(self):
+ self.reset()
+
+ def reset(self):
+ self._items = {}
+
+ def register_item(self, key, item):
+ """Add item to the registry."""
+ if key in self._items:
+ raise ValueError(
+ 'Item already registered with key {0}'.format(key))
+ self._items[key] = item
+
+ def unregister_item(self, key, force=True):
+ """Remove item from the registry."""
+ if key in self._items:
+ del self._items[key]
+ elif not force:
+ raise KeyError("%s: key not present to unregister" % key)
+
+ @property
+ def registered_items(self):
+ """All the items that have been registered.
+
+ This cannot be used to modify the contents of the registry.
+ """
+ return copy.copy(self._items)
diff --git a/cloudinit/reporting/__init__.py b/cloudinit/reporting/__init__.py
new file mode 100644
index 00000000..6b41ae61
--- /dev/null
+++ b/cloudinit/reporting/__init__.py
@@ -0,0 +1,42 @@
+# Copyright 2015 Canonical Ltd.
+# This file is part of cloud-init. See LICENCE file for license information.
+#
+"""
+cloud-init reporting framework
+
+The reporting framework is intended to allow all parts of cloud-init to
+report events in a structured manner.
+"""
+
+from ..registry import DictRegistry
+from .handlers import available_handlers
+
+DEFAULT_CONFIG = {
+ 'logging': {'type': 'log'},
+}
+
+
+def update_configuration(config):
+ """Update the instanciated_handler_registry.
+
+ :param config:
+ The dictionary containing changes to apply. If a key is given
+ with a False-ish value, the registered handler matching that name
+ will be unregistered.
+ """
+ for handler_name, handler_config in config.items():
+ if not handler_config:
+ instantiated_handler_registry.unregister_item(
+ handler_name, force=True)
+ continue
+ handler_config = handler_config.copy()
+ cls = available_handlers.registered_items[handler_config.pop('type')]
+ instantiated_handler_registry.unregister_item(handler_name)
+ instance = cls(**handler_config)
+ instantiated_handler_registry.register_item(handler_name, instance)
+
+
+instantiated_handler_registry = DictRegistry()
+update_configuration(DEFAULT_CONFIG)
+
+# vi: ts=4 expandtab
diff --git a/cloudinit/reporting/events.py b/cloudinit/reporting/events.py
new file mode 100644
index 00000000..2f767f64
--- /dev/null
+++ b/cloudinit/reporting/events.py
@@ -0,0 +1,246 @@
+# Copyright 2015 Canonical Ltd.
+# This file is part of cloud-init. See LICENCE file for license information.
+#
+"""
+events for reporting.
+
+The events here are designed to be used with reporting.
+They can be published to registered handlers with report_event.
+"""
+import base64
+import os.path
+import time
+
+from . import instantiated_handler_registry
+
+FINISH_EVENT_TYPE = 'finish'
+START_EVENT_TYPE = 'start'
+
+DEFAULT_EVENT_ORIGIN = 'cloudinit'
+
+
+class _nameset(set):
+ def __getattr__(self, name):
+ if name in self:
+ return name
+ raise AttributeError("%s not a valid value" % name)
+
+
+status = _nameset(("SUCCESS", "WARN", "FAIL"))
+
+
+class ReportingEvent(object):
+ """Encapsulation of event formatting."""
+
+ def __init__(self, event_type, name, description,
+ origin=DEFAULT_EVENT_ORIGIN, timestamp=time.time()):
+ self.event_type = event_type
+ self.name = name
+ self.description = description
+ self.origin = origin
+ self.timestamp = timestamp
+
+ def as_string(self):
+ """The event represented as a string."""
+ return '{0}: {1}: {2}'.format(
+ self.event_type, self.name, self.description)
+
+ def as_dict(self):
+ """The event represented as a dictionary."""
+ return {'name': self.name, 'description': self.description,
+ 'event_type': self.event_type, 'origin': self.origin,
+ 'timestamp': self.timestamp}
+
+
+class FinishReportingEvent(ReportingEvent):
+
+ def __init__(self, name, description, result=status.SUCCESS,
+ post_files=None):
+ super(FinishReportingEvent, self).__init__(
+ FINISH_EVENT_TYPE, name, description)
+ self.result = result
+ if post_files is None:
+ post_files = []
+ self.post_files = post_files
+ if result not in status:
+ raise ValueError("Invalid result: %s" % result)
+
+ def as_string(self):
+ return '{0}: {1}: {2}: {3}'.format(
+ self.event_type, self.name, self.result, self.description)
+
+ def as_dict(self):
+ """The event represented as json friendly."""
+ data = super(FinishReportingEvent, self).as_dict()
+ data['result'] = self.result
+ if self.post_files:
+ data['files'] = _collect_file_info(self.post_files)
+ return data
+
+
+def report_event(event):
+ """Report an event to all registered event handlers.
+
+ This should generally be called via one of the other functions in
+ the reporting module.
+
+ :param event_type:
+ The type of the event; this should be a constant from the
+ reporting module.
+ """
+ for _, handler in instantiated_handler_registry.registered_items.items():
+ handler.publish_event(event)
+
+
+def report_finish_event(event_name, event_description,
+ result=status.SUCCESS, post_files=None):
+ """Report a "finish" event.
+
+ See :py:func:`.report_event` for parameter details.
+ """
+ event = FinishReportingEvent(event_name, event_description, result,
+ post_files=post_files)
+ return report_event(event)
+
+
+def report_start_event(event_name, event_description):
+ """Report a "start" event.
+
+ :param event_name:
+ The name of the event; this should be a topic which events would
+ share (e.g. it will be the same for start and finish events).
+
+ :param event_description:
+ A human-readable description of the event that has occurred.
+ """
+ event = ReportingEvent(START_EVENT_TYPE, event_name, event_description)
+ return report_event(event)
+
+
+class ReportEventStack(object):
+ """Context Manager for using :py:func:`report_event`
+
+ This enables calling :py:func:`report_start_event` and
+ :py:func:`report_finish_event` through a context manager.
+
+ :param name:
+ the name of the event
+
+ :param description:
+ the event's description, passed on to :py:func:`report_start_event`
+
+ :param message:
+ the description to use for the finish event. defaults to
+ :param:description.
+
+ :param parent:
+ :type parent: :py:class:ReportEventStack or None
+ The parent of this event. The parent is populated with
+ results of all its children. The name used in reporting
+ is <parent.name>/<name>
+
+ :param reporting_enabled:
+ Indicates if reporting events should be generated.
+ If not provided, defaults to the parent's value, or True if no parent
+ is provided.
+
+ :param result_on_exception:
+ The result value to set if an exception is caught. default
+ value is FAIL.
+ """
+ def __init__(self, name, description, message=None, parent=None,
+ reporting_enabled=None, result_on_exception=status.FAIL,
+ post_files=None):
+ self.parent = parent
+ self.name = name
+ self.description = description
+ self.message = message
+ self.result_on_exception = result_on_exception
+ self.result = status.SUCCESS
+ if post_files is None:
+ post_files = []
+ self.post_files = post_files
+
+ # use parents reporting value if not provided
+ if reporting_enabled is None:
+ if parent:
+ reporting_enabled = parent.reporting_enabled
+ else:
+ reporting_enabled = True
+ self.reporting_enabled = reporting_enabled
+
+ if parent:
+ self.fullname = '/'.join((parent.fullname, name,))
+ else:
+ self.fullname = self.name
+ self.children = {}
+
+ def __repr__(self):
+ return ("ReportEventStack(%s, %s, reporting_enabled=%s)" %
+ (self.name, self.description, self.reporting_enabled))
+
+ def __enter__(self):
+ self.result = status.SUCCESS
+ if self.reporting_enabled:
+ report_start_event(self.fullname, self.description)
+ if self.parent:
+ self.parent.children[self.name] = (None, None)
+ return self
+
+ def _childrens_finish_info(self):
+ for cand_result in (status.FAIL, status.WARN):
+ for name, (value, msg) in self.children.items():
+ if value == cand_result:
+ return (value, self.message)
+ return (self.result, self.message)
+
+ @property
+ def result(self):
+ return self._result
+
+ @result.setter
+ def result(self, value):
+ if value not in status:
+ raise ValueError("'%s' not a valid result" % value)
+ self._result = value
+
+ @property
+ def message(self):
+ if self._message is not None:
+ return self._message
+ return self.description
+
+ @message.setter
+ def message(self, value):
+ self._message = value
+
+ def _finish_info(self, exc):
+ # return tuple of description, and value
+ if exc:
+ return (self.result_on_exception, self.message)
+ return self._childrens_finish_info()
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ (result, msg) = self._finish_info(exc_value)
+ if self.parent:
+ self.parent.children[self.name] = (result, msg)
+ if self.reporting_enabled:
+ report_finish_event(self.fullname, msg, result,
+ post_files=self.post_files)
+
+
+def _collect_file_info(files):
+ if not files:
+ return None
+ ret = []
+ for fname in files:
+ if not os.path.isfile(fname):
+ content = None
+ else:
+ with open(fname, "rb") as fp:
+ content = base64.b64encode(fp.read()).decode()
+ ret.append({'path': fname, 'content': content,
+ 'encoding': 'base64'})
+ return ret
+
+# vi: ts=4 expandtab syntax=python
diff --git a/cloudinit/reporting/handlers.py b/cloudinit/reporting/handlers.py
new file mode 100644
index 00000000..3212d173
--- /dev/null
+++ b/cloudinit/reporting/handlers.py
@@ -0,0 +1,91 @@
+# vi: ts=4 expandtab
+
+import abc
+import json
+import six
+
+from ..registry import DictRegistry
+from .. import (url_helper, util)
+from .. import log as logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+@six.add_metaclass(abc.ABCMeta)
+class ReportingHandler(object):
+ """Base class for report handlers.
+
+ Implement :meth:`~publish_event` for controlling what
+ the handler does with an event.
+ """
+
+ @abc.abstractmethod
+ def publish_event(self, event):
+ """Publish an event."""
+
+
+class LogHandler(ReportingHandler):
+ """Publishes events to the cloud-init log at the ``DEBUG`` log level."""
+
+ def __init__(self, level="DEBUG"):
+ super(LogHandler, self).__init__()
+ if isinstance(level, int):
+ pass
+ else:
+ input_level = level
+ try:
+ level = getattr(logging, level.upper())
+ except:
+ LOG.warn("invalid level '%s', using WARN", input_level)
+ level = logging.WARN
+ self.level = level
+
+ def publish_event(self, event):
+ logger = logging.getLogger(
+ '.'.join(['cloudinit', 'reporting', event.event_type, event.name]))
+ logger.log(self.level, event.as_string())
+
+
+class PrintHandler(ReportingHandler):
+ """Print the event as a string."""
+
+ def publish_event(self, event):
+ print(event.as_string())
+
+
+class WebHookHandler(ReportingHandler):
+ def __init__(self, endpoint, consumer_key=None, token_key=None,
+ token_secret=None, consumer_secret=None, timeout=None,
+ retries=None):
+ super(WebHookHandler, self).__init__()
+
+ if any([consumer_key, token_key, token_secret, consumer_secret]):
+ self.oauth_helper = url_helper.OauthUrlHelper(
+ consumer_key=consumer_key, token_key=token_key,
+ token_secret=token_secret, consumer_secret=consumer_secret)
+ else:
+ self.oauth_helper = None
+ self.endpoint = endpoint
+ self.timeout = timeout
+ self.retries = retries
+ self.ssl_details = util.fetch_ssl_details()
+
+ def publish_event(self, event):
+ if self.oauth_helper:
+ readurl = self.oauth_helper.readurl
+ else:
+ readurl = url_helper.readurl
+ try:
+ return readurl(
+ self.endpoint, data=json.dumps(event.as_dict()),
+ timeout=self.timeout,
+ retries=self.retries, ssl_details=self.ssl_details)
+ except:
+ LOG.warn("failed posting event: %s" % event.as_string())
+
+
+available_handlers = DictRegistry()
+available_handlers.register_item('log', LogHandler)
+available_handlers.register_item('print', PrintHandler)
+available_handlers.register_item('webhook', WebHookHandler)
diff --git a/cloudinit/settings.py b/cloudinit/settings.py
index 5efcb0b0..8c258ea1 100644
--- a/cloudinit/settings.py
+++ b/cloudinit/settings.py
@@ -42,12 +42,13 @@ CFG_BUILTIN = {
'CloudSigma',
'CloudStack',
'SmartOS',
+ 'Bigstep',
# At the end to act as a 'catch' when none of the above work...
'None',
],
'def_log_file': '/var/log/cloud-init.log',
'log_cfgs': [],
- 'syslog_fix_perms': 'syslog:adm',
+ 'syslog_fix_perms': ['syslog:adm', 'root:adm'],
'system_info': {
'paths': {
'cloud_dir': '/var/lib/cloud',
diff --git a/cloudinit/signal_handler.py b/cloudinit/signal_handler.py
index 40b0c94c..0d95f506 100644
--- a/cloudinit/signal_handler.py
+++ b/cloudinit/signal_handler.py
@@ -22,7 +22,7 @@ import inspect
import signal
import sys
-from StringIO import StringIO
+from six import StringIO
from cloudinit import log as logging
from cloudinit import util
diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py
index 1e913a6e..cd61df31 100644
--- a/cloudinit/sources/DataSourceAltCloud.py
+++ b/cloudinit/sources/DataSourceAltCloud.py
@@ -40,9 +40,8 @@ LOG = logging.getLogger(__name__)
CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info'
# Shell command lists
-CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name']
CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy']
-CMD_UDEVADM_SETTLE = ['/sbin/udevadm', 'settle', '--quiet', '--timeout=5']
+CMD_UDEVADM_SETTLE = ['/sbin/udevadm', 'settle', '--timeout=5']
META_DATA_NOT_SUPPORTED = {
'block-device-mapping': {},
@@ -100,11 +99,7 @@ class DataSourceAltCloud(sources.DataSource):
'''
Description:
Get the type for the cloud back end this instance is running on
- by examining the string returned by:
- dmidecode --string system-product-name
-
- On VMWare/vSphere dmidecode returns: RHEV Hypervisor
- On VMWare/vSphere dmidecode returns: VMware Virtual Platform
+ by examining the string returned by reading the dmi data.
Input:
None
@@ -117,26 +112,20 @@ class DataSourceAltCloud(sources.DataSource):
uname_arch = os.uname()[4]
if uname_arch.startswith("arm") or uname_arch == "aarch64":
- # Disabling because dmidecode in CMD_DMI_SYSTEM crashes kvm process
+ # Disabling because dmi data is not available on ARM processors
LOG.debug("Disabling AltCloud datasource on arm (LP: #1243287)")
return 'UNKNOWN'
- cmd = CMD_DMI_SYSTEM
- try:
- (cmd_out, _err) = util.subp(cmd)
- except ProcessExecutionError, _err:
- LOG.debug(('Failed command: %s\n%s') % \
- (' '.join(cmd), _err.message))
- return 'UNKNOWN'
- except OSError, _err:
- LOG.debug(('Failed command: %s\n%s') % \
- (' '.join(cmd), _err.message))
+ system_name = util.read_dmi_data("system-product-name")
+ if not system_name:
return 'UNKNOWN'
- if cmd_out.upper().startswith('RHEV'):
+ sys_name = system_name.upper()
+
+ if sys_name.startswith('RHEV'):
return 'RHEV'
- if cmd_out.upper().startswith('VMWARE'):
+ if sys_name.startswith('VMWARE'):
return 'VSPHERE'
return 'UNKNOWN'
@@ -211,11 +200,11 @@ class DataSourceAltCloud(sources.DataSource):
cmd = CMD_PROBE_FLOPPY
(cmd_out, _err) = util.subp(cmd)
LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out))
- except ProcessExecutionError, _err:
+ except ProcessExecutionError as _err:
util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd),
_err.message)
return False
- except OSError, _err:
+ except OSError as _err:
util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd),
_err.message)
return False
@@ -228,11 +217,11 @@ class DataSourceAltCloud(sources.DataSource):
cmd.append('--exit-if-exists=' + floppy_dev)
(cmd_out, _err) = util.subp(cmd)
LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out))
- except ProcessExecutionError, _err:
+ except ProcessExecutionError as _err:
util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd),
_err.message)
return False
- except OSError, _err:
+ except OSError as _err:
util.logexc(LOG, 'Failed command: %s\n%s', ' '.join(cmd),
_err.message)
return False
@@ -295,7 +284,7 @@ class DataSourceAltCloud(sources.DataSource):
# In the future 'dsmode' like behavior can be added to offer user
# the ability to run before networking.
datasources = [
- (DataSourceAltCloud, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceAltCloud, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
]
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
index 09bc196d..698f4cac 100644
--- a/cloudinit/sources/DataSourceAzure.py
+++ b/cloudinit/sources/DataSourceAzure.py
@@ -17,26 +17,30 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
+import contextlib
import crypt
import fnmatch
import os
import os.path
import time
+import xml.etree.ElementTree as ET
+
from xml.dom import minidom
from cloudinit import log as logging
from cloudinit.settings import PER_ALWAYS
from cloudinit import sources
from cloudinit import util
+from cloudinit.sources.helpers.azure import get_metadata_from_fabric
LOG = logging.getLogger(__name__)
DS_NAME = 'Azure'
DEFAULT_METADATA = {"instance-id": "iid-AZURE-NODE"}
AGENT_START = ['service', 'walinuxagent', 'start']
-BOUNCE_COMMAND = ['sh', '-xc',
+BOUNCE_COMMAND = [
+ 'sh', '-xc',
"i=$interface; x=0; ifdown $i || x=$?; ifup $i || x=$?; exit $x"]
-DATA_DIR_CLEAN_LIST = ['SharedConfig.xml']
BUILTIN_DS_CONFIG = {
'agent_command': AGENT_START,
@@ -53,9 +57,9 @@ BUILTIN_DS_CONFIG = {
BUILTIN_CLOUD_CONFIG = {
'disk_setup': {
- 'ephemeral0': {'table_type': 'mbr',
- 'layout': True,
- 'overwrite': False},
+ 'ephemeral0': {'table_type': 'gpt',
+ 'layout': [100],
+ 'overwrite': True},
},
'fs_setup': [{'filesystem': 'ext4',
'device': 'ephemeral0.1',
@@ -65,6 +69,40 @@ BUILTIN_CLOUD_CONFIG = {
DS_CFG_PATH = ['datasource', DS_NAME]
DEF_EPHEMERAL_LABEL = 'Temporary Storage'
+# The redacted password fails to meet password complexity requirements
+# so we can safely use this to mask/redact the password in the ovf-env.xml
+DEF_PASSWD_REDACTION = 'REDACTED'
+
+
+def get_hostname(hostname_command='hostname'):
+ return util.subp(hostname_command, capture=True)[0].strip()
+
+
+def set_hostname(hostname, hostname_command='hostname'):
+ util.subp([hostname_command, hostname])
+
+
+@contextlib.contextmanager
+def temporary_hostname(temp_hostname, cfg, hostname_command='hostname'):
+ """
+ Set a temporary hostname, restoring the previous hostname on exit.
+
+ Will have the value of the previous hostname when used as a context
+ manager, or None if the hostname was not changed.
+ """
+ policy = cfg['hostname_bounce']['policy']
+ previous_hostname = get_hostname(hostname_command)
+ if (not util.is_true(cfg.get('set_hostname')) or
+ util.is_false(policy) or
+ (previous_hostname == temp_hostname and policy != 'force')):
+ yield None
+ return
+ set_hostname(temp_hostname, hostname_command)
+ try:
+ yield previous_hostname
+ finally:
+ set_hostname(previous_hostname, hostname_command)
+
class DataSourceAzureNet(sources.DataSource):
def __init__(self, sys_cfg, distro, paths):
@@ -80,6 +118,54 @@ class DataSourceAzureNet(sources.DataSource):
root = sources.DataSource.__str__(self)
return "%s [seed=%s]" % (root, self.seed)
+ def get_metadata_from_agent(self):
+ temp_hostname = self.metadata.get('local-hostname')
+ hostname_command = self.ds_cfg['hostname_bounce']['hostname_command']
+ with temporary_hostname(temp_hostname, self.ds_cfg,
+ hostname_command=hostname_command) \
+ as previous_hostname:
+ if (previous_hostname is not None and
+ util.is_true(self.ds_cfg.get('set_hostname'))):
+ cfg = self.ds_cfg['hostname_bounce']
+ try:
+ perform_hostname_bounce(hostname=temp_hostname,
+ cfg=cfg,
+ prev_hostname=previous_hostname)
+ except Exception as e:
+ LOG.warn("Failed publishing hostname: %s", e)
+ util.logexc(LOG, "handling set_hostname failed")
+
+ try:
+ invoke_agent(self.ds_cfg['agent_command'])
+ except util.ProcessExecutionError:
+ # claim the datasource even if the command failed
+ util.logexc(LOG, "agent command '%s' failed.",
+ self.ds_cfg['agent_command'])
+
+ ddir = self.ds_cfg['data_dir']
+
+ fp_files = []
+ key_value = None
+ for pk in self.cfg.get('_pubkeys', []):
+ if pk.get('value', None):
+ key_value = pk['value']
+ LOG.debug("ssh authentication: using value from fabric")
+ else:
+ bname = str(pk['fingerprint'] + ".crt")
+ fp_files += [os.path.join(ddir, bname)]
+ LOG.debug("ssh authentication: "
+ "using fingerprint from fabirc")
+
+ missing = util.log_time(logfunc=LOG.debug, msg="waiting for files",
+ func=wait_for_files,
+ args=(fp_files,))
+ if len(missing):
+ LOG.warn("Did not find files, but going on: %s", missing)
+
+ metadata = {}
+ metadata['public-keys'] = key_value or pubkeys_from_crt_files(fp_files)
+ return metadata
+
def get_data(self):
# azure removes/ejects the cdrom containing the ovf-env.xml
# file on reboot. So, in order to successfully reboot we
@@ -124,77 +210,34 @@ class DataSourceAzureNet(sources.DataSource):
LOG.debug("using files cached in %s", ddir)
# azure / hyper-v provides random data here
- seed = util.load_file("/sys/firmware/acpi/tables/OEM0", quiet=True)
+ seed = util.load_file("/sys/firmware/acpi/tables/OEM0",
+ quiet=True, decode=False)
if seed:
self.metadata['random_seed'] = seed
# now update ds_cfg to reflect contents pass in config
user_ds_cfg = util.get_cfg_by_path(self.cfg, DS_CFG_PATH, {})
self.ds_cfg = util.mergemanydict([user_ds_cfg, self.ds_cfg])
- mycfg = self.ds_cfg
- ddir = mycfg['data_dir']
-
- if found != ddir:
- cached_ovfenv = util.load_file(
- os.path.join(ddir, 'ovf-env.xml'), quiet=True)
- if cached_ovfenv != files['ovf-env.xml']:
- # source was not walinux-agent's datadir, so we have to clean
- # up so 'wait_for_files' doesn't return early due to stale data
- cleaned = []
- for f in [os.path.join(ddir, f) for f in DATA_DIR_CLEAN_LIST]:
- if os.path.exists(f):
- util.del_file(f)
- cleaned.append(f)
- if cleaned:
- LOG.info("removed stale file(s) in '%s': %s",
- ddir, str(cleaned))
# walinux agent writes files world readable, but expects
# the directory to be protected.
- write_files(ddir, files, dirmode=0700)
-
- # handle the hostname 'publishing'
- try:
- handle_set_hostname(mycfg.get('set_hostname'),
- self.metadata.get('local-hostname'),
- mycfg['hostname_bounce'])
- except Exception as e:
- LOG.warn("Failed publishing hostname: %s", e)
- util.logexc(LOG, "handling set_hostname failed")
-
- try:
- invoke_agent(mycfg['agent_command'])
- except util.ProcessExecutionError:
- # claim the datasource even if the command failed
- util.logexc(LOG, "agent command '%s' failed.",
- mycfg['agent_command'])
-
- shcfgxml = os.path.join(ddir, "SharedConfig.xml")
- wait_for = [shcfgxml]
-
- fp_files = []
- for pk in self.cfg.get('_pubkeys', []):
- bname = str(pk['fingerprint'] + ".crt")
- fp_files += [os.path.join(ddir, bname)]
+ write_files(ddir, files, dirmode=0o700)
- missing = util.log_time(logfunc=LOG.debug, msg="waiting for files",
- func=wait_for_files,
- args=(wait_for + fp_files,))
- if len(missing):
- LOG.warn("Did not find files, but going on: %s", missing)
-
- if shcfgxml in missing:
- LOG.warn("SharedConfig.xml missing, using static instance-id")
+ if self.ds_cfg['agent_command'] == '__builtin__':
+ metadata_func = get_metadata_from_fabric
else:
- try:
- self.metadata['instance-id'] = iid_from_shared_config(shcfgxml)
- except ValueError as e:
- LOG.warn("failed to get instance id in %s: %s", shcfgxml, e)
+ metadata_func = self.get_metadata_from_agent
+ try:
+ fabric_data = metadata_func()
+ except Exception as exc:
+ LOG.info("Error communicating with Azure fabric; assume we aren't"
+ " on Azure.", exc_info=True)
+ return False
- pubkeys = pubkeys_from_crt_files(fp_files)
- self.metadata['public-keys'] = pubkeys
+ self.metadata['instance-id'] = util.read_dmi_data('system-uuid')
+ self.metadata.update(fabric_data)
- found_ephemeral = find_ephemeral_disk()
+ found_ephemeral = find_fabric_formatted_ephemeral_disk()
if found_ephemeral:
self.ds_cfg['disk_aliases']['ephemeral0'] = found_ephemeral
LOG.debug("using detected ephemeral0 of %s", found_ephemeral)
@@ -211,35 +254,42 @@ class DataSourceAzureNet(sources.DataSource):
def get_config_obj(self):
return self.cfg
+ def check_instance_id(self, sys_cfg):
+ # quickly (local check only) if self.instance_id is still valid
+ return sources.instance_id_matches_system_uuid(self.get_instance_id())
+
def count_files(mp):
return len(fnmatch.filter(os.listdir(mp), '*[!cdrom]*'))
-def find_ephemeral_part():
+def find_fabric_formatted_ephemeral_part():
"""
- Locate the default ephmeral0.1 device. This will be the first device
- that has a LABEL of DEF_EPHEMERAL_LABEL and is a NTFS device. If Azure
- gets more ephemeral devices, this logic will only identify the first
- such device.
+ Locate the first fabric formatted ephemeral device.
"""
- c_label_devs = util.find_devs_with("LABEL=%s" % DEF_EPHEMERAL_LABEL)
- c_fstype_devs = util.find_devs_with("TYPE=ntfs")
- for dev in c_label_devs:
- if dev in c_fstype_devs:
- return dev
+ potential_locations = ['/dev/disk/cloud/azure_resource-part1',
+ '/dev/disk/azure/resource-part1']
+ device_location = None
+ for potential_location in potential_locations:
+ if os.path.exists(potential_location):
+ device_location = potential_location
+ break
+ if device_location is None:
+ return None
+ ntfs_devices = util.find_devs_with("TYPE=ntfs")
+ real_device = os.path.realpath(device_location)
+ if real_device in ntfs_devices:
+ return device_location
return None
-def find_ephemeral_disk():
+def find_fabric_formatted_ephemeral_disk():
"""
Get the ephemeral disk.
"""
- part_dev = find_ephemeral_part()
- if part_dev and str(part_dev[-1]).isdigit():
- return part_dev[:-1]
- elif part_dev:
- return part_dev
+ part_dev = find_fabric_formatted_ephemeral_part()
+ if part_dev:
+ return part_dev.split('-')[0]
return None
@@ -253,7 +303,7 @@ def support_new_ephemeral(cfg):
new ephemeral device is detected, cloud-init overrides the default
frequency for both disk-setup and mounts for the current boot only.
"""
- device = find_ephemeral_part()
+ device = find_fabric_formatted_ephemeral_part()
if not device:
LOG.debug("no default fabric formated ephemeral0.1 found")
return None
@@ -298,39 +348,15 @@ def support_new_ephemeral(cfg):
return mod_list
-def handle_set_hostname(enabled, hostname, cfg):
- if not util.is_true(enabled):
- return
-
- if not hostname:
- LOG.warn("set_hostname was true but no local-hostname")
- return
-
- apply_hostname_bounce(hostname=hostname, policy=cfg['policy'],
- interface=cfg['interface'],
- command=cfg['command'],
- hostname_command=cfg['hostname_command'])
-
-
-def apply_hostname_bounce(hostname, policy, interface, command,
- hostname_command="hostname"):
+def perform_hostname_bounce(hostname, cfg, prev_hostname):
# set the hostname to 'hostname' if it is not already set to that.
# then, if policy is not off, bounce the interface using command
- prev_hostname = util.subp(hostname_command, capture=True)[0].strip()
-
- util.subp([hostname_command, hostname])
-
- msg = ("phostname=%s hostname=%s policy=%s interface=%s" %
- (prev_hostname, hostname, policy, interface))
-
- if util.is_false(policy):
- LOG.debug("pubhname: policy false, skipping [%s]", msg)
- return
-
- if prev_hostname == hostname and policy != "force":
- LOG.debug("pubhname: no change, policy != force. skipping. [%s]", msg)
- return
+ command = cfg['command']
+ interface = cfg['interface']
+ policy = cfg['policy']
+ msg = ("hostname=%s policy=%s interface=%s" %
+ (hostname, policy, interface))
env = os.environ.copy()
env['interface'] = interface
env['hostname'] = hostname
@@ -343,15 +369,16 @@ def apply_hostname_bounce(hostname, policy, interface, command,
shell = not isinstance(command, (list, tuple))
# capture=False, see comments in bug 1202758 and bug 1206164.
util.log_time(logfunc=LOG.debug, msg="publishing hostname",
- get_uptime=True, func=util.subp,
- kwargs={'args': command, 'shell': shell, 'capture': False,
- 'env': env})
+ get_uptime=True, func=util.subp,
+ kwargs={'args': command, 'shell': shell, 'capture': False,
+ 'env': env})
-def crtfile_to_pubkey(fname):
+def crtfile_to_pubkey(fname, data=None):
pipeline = ('openssl x509 -noout -pubkey < "$0" |'
'ssh-keygen -i -m PKCS8 -f /dev/stdin')
- (out, _err) = util.subp(['sh', '-c', pipeline, fname], capture=True)
+ (out, _err) = util.subp(['sh', '-c', pipeline, fname],
+ capture=True, data=data)
return out.rstrip()
@@ -383,14 +410,30 @@ def wait_for_files(flist, maxwait=60, naplen=.5):
def write_files(datadir, files, dirmode=None):
+
+ def _redact_password(cnt, fname):
+ """Azure provides the UserPassword in plain text. So we redact it"""
+ try:
+ root = ET.fromstring(cnt)
+ for elem in root.iter():
+ if ('UserPassword' in elem.tag and
+ elem.text != DEF_PASSWD_REDACTION):
+ elem.text = DEF_PASSWD_REDACTION
+ return ET.tostring(root)
+ except Exception:
+ LOG.critical("failed to redact userpassword in {}".format(fname))
+ return cnt
+
if not datadir:
return
if not files:
files = {}
util.ensure_dir(datadir, dirmode)
for (name, content) in files.items():
- util.write_file(filename=os.path.join(datadir, name),
- content=content, mode=0600)
+ fname = os.path.join(datadir, name)
+ if 'ovf-env.xml' in name:
+ content = _redact_password(content, fname)
+ util.write_file(filename=fname, content=content, mode=0o600)
def invoke_agent(cmd):
@@ -441,7 +484,8 @@ def load_azure_ovf_pubkeys(sshnode):
for pk_node in pubkeys:
if not pk_node.hasChildNodes():
continue
- cur = {'fingerprint': "", 'path': ""}
+
+ cur = {'fingerprint': "", 'path': "", 'value': ""}
for child in pk_node.childNodes:
if child.nodeType == text_node or not child.localName:
continue
@@ -461,20 +505,6 @@ def load_azure_ovf_pubkeys(sshnode):
return found
-def single_node_at_path(node, pathlist):
- curnode = node
- for tok in pathlist:
- results = find_child(curnode, lambda n: n.localName == tok)
- if len(results) == 0:
- raise ValueError("missing %s token in %s" % (tok, str(pathlist)))
- if len(results) > 1:
- raise ValueError("found %s nodes of type %s looking for %s" %
- (len(results), tok, str(pathlist)))
- curnode = results[0]
-
- return curnode
-
-
def read_azure_ovf(contents):
try:
dom = minidom.parseString(contents)
@@ -482,7 +512,7 @@ def read_azure_ovf(contents):
raise BrokenAzureDataSource("invalid xml: %s" % e)
results = find_child(dom.documentElement,
- lambda n: n.localName == "ProvisioningSection")
+ lambda n: n.localName == "ProvisioningSection")
if len(results) == 0:
raise NonAzureDataSource("No ProvisioningSection")
@@ -492,7 +522,8 @@ def read_azure_ovf(contents):
provSection = results[0]
lpcs_nodes = find_child(provSection,
- lambda n: n.localName == "LinuxProvisioningConfigurationSet")
+ lambda n:
+ n.localName == "LinuxProvisioningConfigurationSet")
if len(results) == 0:
raise NonAzureDataSource("No LinuxProvisioningConfigurationSet")
@@ -559,7 +590,7 @@ def read_azure_ovf(contents):
defuser = {}
if username:
defuser['name'] = username
- if password:
+ if password and DEF_PASSWD_REDACTION != password:
defuser['passwd'] = encrypt_pass(password)
defuser['lock_passwd'] = False
@@ -592,32 +623,13 @@ def load_azure_ds_dir(source_dir):
if not os.path.isfile(ovf_file):
raise NonAzureDataSource("No ovf-env file found")
- with open(ovf_file, "r") as fp:
+ with open(ovf_file, "rb") as fp:
contents = fp.read()
md, ud, cfg = read_azure_ovf(contents)
return (md, ud, cfg, {'ovf-env.xml': contents})
-def iid_from_shared_config(path):
- with open(path, "rb") as fp:
- content = fp.read()
- return iid_from_shared_config_content(content)
-
-
-def iid_from_shared_config_content(content):
- """
- find INSTANCE_ID in:
- <?xml version="1.0" encoding="utf-8"?>
- <SharedConfig version="1.0.0.0" goalStateIncarnation="1">
- <Deployment name="INSTANCE_ID" guid="{...}" incarnation="0">
- <Service name="..." guid="{00000000-0000-0000-0000-000000000000}" />
- """
- dom = minidom.parseString(content)
- depnode = single_node_at_path(dom, ["SharedConfig", "Deployment"])
- return depnode.attributes.get('name').value
-
-
class BrokenAzureDataSource(Exception):
pass
@@ -628,7 +640,7 @@ class NonAzureDataSource(Exception):
# Used to match classes to dependencies
datasources = [
- (DataSourceAzureNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceAzureNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
]
diff --git a/cloudinit/sources/DataSourceBigstep.py b/cloudinit/sources/DataSourceBigstep.py
new file mode 100644
index 00000000..b5ee4129
--- /dev/null
+++ b/cloudinit/sources/DataSourceBigstep.py
@@ -0,0 +1,57 @@
+#
+# Copyright (C) 2015-2016 Bigstep Cloud Ltd.
+#
+# Author: Alexandru Sirbu <alexandru.sirbu@bigstep.com>
+#
+
+import json
+import errno
+
+from cloudinit import log as logging
+from cloudinit import sources
+from cloudinit import util
+from cloudinit import url_helper
+
+LOG = logging.getLogger(__name__)
+
+
+class DataSourceBigstep(sources.DataSource):
+ def __init__(self, sys_cfg, distro, paths):
+ sources.DataSource.__init__(self, sys_cfg, distro, paths)
+ self.metadata = {}
+ self.vendordata_raw = ""
+ self.userdata_raw = ""
+
+ def get_data(self, apply_filter=False):
+ url = get_url_from_file()
+ if url is None:
+ return False
+ response = url_helper.readurl(url)
+ decoded = json.loads(response.contents)
+ self.metadata = decoded["metadata"]
+ self.vendordata_raw = decoded["vendordata_raw"]
+ self.userdata_raw = decoded["userdata_raw"]
+ return True
+
+
+def get_url_from_file():
+ try:
+ content = util.load_file("/var/lib/cloud/data/seed/bigstep/url")
+ except IOError as e:
+ # If the file doesn't exist, then the server probably isn't a Bigstep
+ # instance; otherwise, another problem exists which needs investigation
+ if e.errno == errno.ENOENT:
+ return None
+ else:
+ raise
+ return content
+
+# Used to match classes to dependencies
+datasources = [
+ (DataSourceBigstep, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+]
+
+
+# Return a list of data sources that match this set of dependencies
+def get_datasource_list(depends):
+ return sources.list_from_depends(depends, datasources)
diff --git a/cloudinit/sources/DataSourceCloudSigma.py b/cloudinit/sources/DataSourceCloudSigma.py
index 707cd0ce..f8f94759 100644
--- a/cloudinit/sources/DataSourceCloudSigma.py
+++ b/cloudinit/sources/DataSourceCloudSigma.py
@@ -44,27 +44,25 @@ class DataSourceCloudSigma(sources.DataSource):
def is_running_in_cloudsigma(self):
"""
- Uses dmidecode to detect if this instance of cloud-init is running
+ Uses dmi data to detect if this instance of cloud-init is running
in the CloudSigma's infrastructure.
"""
uname_arch = os.uname()[4]
if uname_arch.startswith("arm") or uname_arch == "aarch64":
- # Disabling because dmidecode in CMD_DMI_SYSTEM crashes kvm process
+ # Disabling because dmi data on ARM processors
LOG.debug("Disabling CloudSigma datasource on arm (LP: #1243287)")
return False
- dmidecode_path = util.which('dmidecode')
- if not dmidecode_path:
+ LOG.debug("determining hypervisor product name via dmi data")
+ sys_product_name = util.read_dmi_data("system-product-name")
+ if not sys_product_name:
+ LOG.warn("failed to get hypervisor product name via dmi data")
return False
+ else:
+ LOG.debug("detected hypervisor as %s", sys_product_name)
+ return 'cloudsigma' in sys_product_name.lower()
- LOG.debug("Determining hypervisor product name via dmidecode")
- try:
- cmd = [dmidecode_path, "--string", "system-product-name"]
- system_product_name, _ = util.subp(cmd)
- return 'cloudsigma' in system_product_name.lower()
- except:
- LOG.warn("Failed to get hypervisor product name via dmidecode")
-
+ LOG.warn("failed to query dmi data for system product name")
return False
def get_data(self):
diff --git a/cloudinit/sources/DataSourceCloudStack.py b/cloudinit/sources/DataSourceCloudStack.py
index 1bbeca59..64595020 100644
--- a/cloudinit/sources/DataSourceCloudStack.py
+++ b/cloudinit/sources/DataSourceCloudStack.py
@@ -26,18 +26,54 @@
import os
import time
+from socket import inet_ntoa
+from struct import pack
from cloudinit import ec2_utils as ec2
from cloudinit import log as logging
-from cloudinit import sources
from cloudinit import url_helper as uhelp
-from cloudinit import util
-from socket import inet_ntoa
-from struct import pack
+from cloudinit import sources, util
LOG = logging.getLogger(__name__)
+class CloudStackPasswordServerClient(object):
+ """
+ Implements password fetching from the CloudStack password server.
+
+ http://cloudstack-administration.readthedocs.org/
+ en/latest/templates.html#adding-password-management-to-your-templates
+ has documentation about the system. This implementation is following that
+ found at
+ https://github.com/shankerbalan/cloudstack-scripts/
+ blob/master/cloud-set-guest-password-debian
+ """
+
+ def __init__(self, virtual_router_address):
+ self.virtual_router_address = virtual_router_address
+
+ def _do_request(self, domu_request):
+ # The password server was in the past, a broken HTTP server, but is now
+ # fixed. wget handles this seamlessly, so it's easier to shell out to
+ # that rather than write our own handling code.
+ output, _ = util.subp([
+ 'wget', '--quiet', '--tries', '3', '--timeout', '20',
+ '--output-document', '-', '--header',
+ 'DomU_Request: {0}'.format(domu_request),
+ '{0}:8080'.format(self.virtual_router_address)
+ ])
+ return output.strip()
+
+ def get_password(self):
+ password = self._do_request('send_my_password')
+ if password in ['', 'saved_password']:
+ return None
+ if password == 'bad_request':
+ raise RuntimeError('Error when attempting to fetch root password.')
+ self._do_request('saved_password')
+ return password
+
+
class DataSourceCloudStack(sources.DataSource):
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
@@ -45,10 +81,11 @@ class DataSourceCloudStack(sources.DataSource):
# Cloudstack has its metadata/userdata URLs located at
# http://<virtual-router-ip>/latest/
self.api_ver = 'latest'
- vr_addr = get_vr_address()
- if not vr_addr:
+ self.vr_addr = get_vr_address()
+ if not self.vr_addr:
raise RuntimeError("No virtual router found!")
- self.metadata_address = "http://%s/" % (vr_addr)
+ self.metadata_address = "http://%s/" % (self.vr_addr,)
+ self.cfg = {}
def _get_url_settings(self):
mcfg = self.ds_cfg
@@ -82,17 +119,20 @@ class DataSourceCloudStack(sources.DataSource):
'latest/meta-data/instance-id')]
start_time = time.time()
url = uhelp.wait_for_url(urls=urls, max_wait=max_wait,
- timeout=timeout, status_cb=LOG.warn)
+ timeout=timeout, status_cb=LOG.warn)
if url:
LOG.debug("Using metadata source: '%s'", url)
else:
LOG.critical(("Giving up on waiting for the metadata from %s"
" after %s seconds"),
- urls, int(time.time() - start_time))
+ urls, int(time.time() - start_time))
return bool(url)
+ def get_config_obj(self):
+ return self.cfg
+
def get_data(self):
seed_ret = {}
if util.read_optional_seed(seed_ret, base=(self.seed_dir + "/")):
@@ -104,12 +144,28 @@ class DataSourceCloudStack(sources.DataSource):
if not self.wait_for_metadata_service():
return False
start_time = time.time()
- self.userdata_raw = ec2.get_instance_userdata(self.api_ver,
- self.metadata_address)
+ self.userdata_raw = ec2.get_instance_userdata(
+ self.api_ver, self.metadata_address)
self.metadata = ec2.get_instance_metadata(self.api_ver,
self.metadata_address)
LOG.debug("Crawl of metadata service took %s seconds",
int(time.time() - start_time))
+ password_client = CloudStackPasswordServerClient(self.vr_addr)
+ try:
+ set_password = password_client.get_password()
+ except Exception:
+ util.logexc(LOG,
+ 'Failed to fetch password from virtual router %s',
+ self.vr_addr)
+ else:
+ if set_password:
+ self.cfg = {
+ 'ssh_pwauth': True,
+ 'password': set_password,
+ 'chpasswd': {
+ 'expire': False,
+ },
+ }
return True
except Exception:
util.logexc(LOG, 'Failed fetching from metadata service %s',
@@ -192,7 +248,7 @@ def get_vr_address():
# Used to match classes to dependencies
datasources = [
- (DataSourceCloudStack, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceCloudStack, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
]
diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py
index 15244a0d..3fa62ef3 100644
--- a/cloudinit/sources/DataSourceConfigDrive.py
+++ b/cloudinit/sources/DataSourceConfigDrive.py
@@ -18,6 +18,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import copy
import os
from cloudinit import log as logging
@@ -39,7 +40,7 @@ FS_TYPES = ('vfat', 'iso9660')
LABEL_TYPES = ('config-2',)
POSSIBLE_MOUNTS = ('sr', 'cd')
OPTICAL_DEVICES = tuple(('/dev/%s%s' % (z, i) for z in POSSIBLE_MOUNTS
- for i in range(0, 2)))
+ for i in range(0, 2)))
class DataSourceConfigDrive(openstack.SourceMixin, sources.DataSource):
@@ -50,6 +51,8 @@ class DataSourceConfigDrive(openstack.SourceMixin, sources.DataSource):
self.seed_dir = os.path.join(paths.seed_dir, 'config_drive')
self.version = None
self.ec2_metadata = None
+ self._network_config = None
+ self.network_json = None
self.files = {}
def __str__(self):
@@ -144,8 +147,25 @@ class DataSourceConfigDrive(openstack.SourceMixin, sources.DataSource):
LOG.warn("Invalid content in vendor-data: %s", e)
self.vendordata_raw = None
+ try:
+ self.network_json = results.get('networkdata')
+ except ValueError as e:
+ LOG.warn("Invalid content in network-data: %s", e)
+ self.network_json = None
+
return True
+ def check_instance_id(self):
+ # quickly (local check only) if self.instance_id is still valid
+ return sources.instance_id_matches_system_uuid(self.get_instance_id())
+
+ @property
+ def network_config(self):
+ if self._network_config is None:
+ if self.network_json is not None:
+ self._network_config = convert_network_data(self.network_json)
+ return self._network_config
+
class DataSourceConfigDriveNet(DataSourceConfigDrive):
def __init__(self, sys_cfg, distro, paths):
@@ -216,11 +236,11 @@ def on_first_boot(data, distro=None):
files = data.get('files', {})
if files:
LOG.debug("Writing %s injected files", len(files))
- for (filename, content) in files.iteritems():
+ for (filename, content) in files.items():
if not filename.startswith(os.sep):
filename = os.sep + filename
try:
- util.write_file(filename, content, mode=0660)
+ util.write_file(filename, content, mode=0o660)
except IOError:
util.logexc(LOG, "Failed writing file: %s", filename)
@@ -283,3 +303,122 @@ datasources = [
# Return a list of data sources that match this set of dependencies
def get_datasource_list(depends):
return sources.list_from_depends(depends, datasources)
+
+
+# Convert OpenStack ConfigDrive NetworkData json to network_config yaml
+def convert_network_data(network_json=None):
+ """Return a dictionary of network_config by parsing provided
+ OpenStack ConfigDrive NetworkData json format
+
+ OpenStack network_data.json provides a 3 element dictionary
+ - "links" (links are network devices, physical or virtual)
+ - "networks" (networks are ip network configurations for one or more
+ links)
+ - services (non-ip services, like dns)
+
+ networks and links are combined via network items referencing specific
+ links via a 'link_id' which maps to a links 'id' field.
+
+ To convert this format to network_config yaml, we first iterate over the
+ links and then walk the network list to determine if any of the networks
+ utilize the current link; if so we generate a subnet entry for the device
+
+ We also need to map network_data.json fields to network_config fields. For
+ example, the network_data links 'id' field is equivalent to network_config
+ 'name' field for devices. We apply more of this mapping to the various
+ link types that we encounter.
+
+ There are additional fields that are populated in the network_data.json
+ from OpenStack that are not relevant to network_config yaml, so we
+ enumerate a dictionary of valid keys for network_yaml and apply filtering
+ to drop these superflous keys from the network_config yaml.
+ """
+ if network_json is None:
+ return None
+
+ # dict of network_config key for filtering network_json
+ valid_keys = {
+ 'physical': [
+ 'name',
+ 'type',
+ 'mac_address',
+ 'subnets',
+ 'params',
+ ],
+ 'subnet': [
+ 'type',
+ 'address',
+ 'netmask',
+ 'broadcast',
+ 'metric',
+ 'gateway',
+ 'pointopoint',
+ 'mtu',
+ 'scope',
+ 'dns_nameservers',
+ 'dns_search',
+ 'routes',
+ ],
+ }
+
+ links = network_json.get('links', [])
+ networks = network_json.get('networks', [])
+ services = network_json.get('services', [])
+
+ config = []
+ for link in links:
+ subnets = []
+ cfg = {k: v for k, v in link.items()
+ if k in valid_keys['physical']}
+ cfg.update({'name': link['id']})
+ for network in [net for net in networks
+ if net['link'] == link['id']]:
+ subnet = {k: v for k, v in network.items()
+ if k in valid_keys['subnet']}
+ if 'dhcp' in network['type']:
+ t = 'dhcp6' if network['type'].startswith('ipv6') else 'dhcp4'
+ subnet.update({
+ 'type': t,
+ })
+ else:
+ subnet.update({
+ 'type': 'static',
+ 'address': network.get('ip_address'),
+ })
+ subnets.append(subnet)
+ cfg.update({'subnets': subnets})
+ if link['type'] in ['ethernet', 'vif', 'ovs', 'phy']:
+ cfg.update({
+ 'type': 'physical',
+ 'mac_address': link['ethernet_mac_address']})
+ elif link['type'] in ['bond']:
+ params = {}
+ for k, v in link.items():
+ if k == 'bond_links':
+ continue
+ elif k.startswith('bond'):
+ params.update({k: v})
+ cfg.update({
+ 'bond_interfaces': copy.deepcopy(link['bond_links']),
+ 'params': params,
+ })
+ elif link['type'] in ['vlan']:
+ cfg.update({
+ 'name': "%s.%s" % (link['vlan_link'],
+ link['vlan_id']),
+ 'vlan_link': link['vlan_link'],
+ 'vlan_id': link['vlan_id'],
+ 'mac_address': link['vlan_mac_address'],
+ })
+ else:
+ raise ValueError(
+ 'Unknown network_data link type: %s' % link['type'])
+
+ config.append(cfg)
+
+ for service in services:
+ cfg = service
+ cfg.update({'type': 'nameserver'})
+ config.append(cfg)
+
+ return {'version': 1, 'config': config}
diff --git a/cloudinit/sources/DataSourceDigitalOcean.py b/cloudinit/sources/DataSourceDigitalOcean.py
index 8f27ee89..12e863d2 100644
--- a/cloudinit/sources/DataSourceDigitalOcean.py
+++ b/cloudinit/sources/DataSourceDigitalOcean.py
@@ -18,7 +18,7 @@ from cloudinit import log as logging
from cloudinit import util
from cloudinit import sources
from cloudinit import ec2_utils
-from types import StringType
+
import functools
@@ -54,9 +54,13 @@ class DataSourceDigitalOcean(sources.DataSource):
def get_data(self):
caller = functools.partial(util.read_file_or_url,
timeout=self.timeout, retries=self.retries)
- md = ec2_utils.MetadataMaterializer(str(caller(self.metadata_address)),
+
+ def mcaller(url):
+ return caller(url).contents
+
+ md = ec2_utils.MetadataMaterializer(mcaller(self.metadata_address),
base_url=self.metadata_address,
- caller=caller)
+ caller=mcaller)
self.metadata = md.materialize()
@@ -72,10 +76,11 @@ class DataSourceDigitalOcean(sources.DataSource):
return "\n".join(self.metadata['vendor-data'])
def get_public_ssh_keys(self):
- if type(self.metadata['public-keys']) is StringType:
- return [self.metadata['public-keys']]
+ public_keys = self.metadata['public-keys']
+ if isinstance(public_keys, list):
+ return public_keys
else:
- return self.metadata['public-keys']
+ return [public_keys]
@property
def availability_zone(self):
@@ -84,7 +89,7 @@ class DataSourceDigitalOcean(sources.DataSource):
def get_instance_id(self):
return self.metadata['id']
- def get_hostname(self, fqdn=False):
+ def get_hostname(self, fqdn=False, resolve_ip=False):
return self.metadata['hostname']
def get_package_mirror_info(self):
@@ -96,8 +101,8 @@ class DataSourceDigitalOcean(sources.DataSource):
# Used to match classes to dependencies
datasources = [
- (DataSourceDigitalOcean, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
- ]
+ (DataSourceDigitalOcean, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+]
# Return a list of data sources that match this set of dependencies
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index 1b20ecf3..3ef2c6af 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -61,12 +61,12 @@ class DataSourceEc2(sources.DataSource):
if not self.wait_for_metadata_service():
return False
start_time = time.time()
- self.userdata_raw = ec2.get_instance_userdata(self.api_ver,
- self.metadata_address)
+ self.userdata_raw = \
+ ec2.get_instance_userdata(self.api_ver, self.metadata_address)
self.metadata = ec2.get_instance_metadata(self.api_ver,
self.metadata_address)
LOG.debug("Crawl of metadata service took %s seconds",
- int(time.time() - start_time))
+ int(time.time() - start_time))
return True
except Exception:
util.logexc(LOG, "Failed reading from metadata address %s",
@@ -132,13 +132,13 @@ class DataSourceEc2(sources.DataSource):
start_time = time.time()
url = uhelp.wait_for_url(urls=urls, max_wait=max_wait,
- timeout=timeout, status_cb=LOG.warn)
+ timeout=timeout, status_cb=LOG.warn)
if url:
LOG.debug("Using metadata source: '%s'", url2base[url])
else:
LOG.critical("Giving up on md from %s after %s seconds",
- urls, int(time.time() - start_time))
+ urls, int(time.time() - start_time))
self.metadata_address = url2base.get(url)
return bool(url)
@@ -156,8 +156,8 @@ class DataSourceEc2(sources.DataSource):
# 'ephemeral0': '/dev/sdb',
# 'root': '/dev/sda1'}
found = None
- bdm_items = self.metadata['block-device-mapping'].iteritems()
- for (entname, device) in bdm_items:
+ bdm = self.metadata['block-device-mapping']
+ for (entname, device) in bdm.items():
if entname == name:
found = device
break
@@ -197,9 +197,16 @@ class DataSourceEc2(sources.DataSource):
except KeyError:
return None
+ @property
+ def region(self):
+ az = self.availability_zone
+ if az is not None:
+ return az[:-1]
+ return None
+
# Used to match classes to dependencies
datasources = [
- (DataSourceEc2, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceEc2, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
]
diff --git a/cloudinit/sources/DataSourceGCE.py b/cloudinit/sources/DataSourceGCE.py
index 2cf8fdcd..7e7fc033 100644
--- a/cloudinit/sources/DataSourceGCE.py
+++ b/cloudinit/sources/DataSourceGCE.py
@@ -30,6 +30,31 @@ BUILTIN_DS_CONFIG = {
REQUIRED_FIELDS = ('instance-id', 'availability-zone', 'local-hostname')
+class GoogleMetadataFetcher(object):
+ headers = {'X-Google-Metadata-Request': True}
+
+ def __init__(self, metadata_address):
+ self.metadata_address = metadata_address
+
+ def get_value(self, path, is_text):
+ value = None
+ try:
+ resp = url_helper.readurl(url=self.metadata_address + path,
+ headers=self.headers)
+ except url_helper.UrlError as exc:
+ msg = "url %s raised exception %s"
+ LOG.debug(msg, path, exc)
+ else:
+ if resp.code == 200:
+ if is_text:
+ value = util.decode_binary(resp.contents)
+ else:
+ value = resp.contents
+ else:
+ LOG.debug("url %s returned code %s", path, resp.code)
+ return value
+
+
class DataSourceGCE(sources.DataSource):
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
@@ -50,18 +75,16 @@ class DataSourceGCE(sources.DataSource):
return public_key
def get_data(self):
- # GCE metadata server requires a custom header since v1
- headers = {'X-Google-Metadata-Request': True}
-
- # url_map: (our-key, path, required)
+ # url_map: (our-key, path, required, is_text)
url_map = [
- ('instance-id', 'instance/id', True),
- ('availability-zone', 'instance/zone', True),
- ('local-hostname', 'instance/hostname', True),
- ('public-keys', 'project/attributes/sshKeys', False),
- ('user-data', 'instance/attributes/user-data', False),
- ('user-data-encoding', 'instance/attributes/user-data-encoding',
- False),
+ ('instance-id', ('instance/id',), True, True),
+ ('availability-zone', ('instance/zone',), True, True),
+ ('local-hostname', ('instance/hostname',), True, True),
+ ('public-keys', ('project/attributes/sshKeys',
+ 'instance/attributes/sshKeys'), False, True),
+ ('user-data', ('instance/attributes/user-data',), False, False),
+ ('user-data-encoding', ('instance/attributes/user-data-encoding',),
+ False, True),
]
# if we cannot resolve the metadata server, then no point in trying
@@ -69,42 +92,34 @@ class DataSourceGCE(sources.DataSource):
LOG.debug("%s is not resolvable", self.metadata_address)
return False
+ metadata_fetcher = GoogleMetadataFetcher(self.metadata_address)
# iterate over url_map keys to get metadata items
- found = False
- for (mkey, path, required) in url_map:
- try:
- resp = url_helper.readurl(url=self.metadata_address + path,
- headers=headers)
- if resp.code == 200:
- found = True
- self.metadata[mkey] = resp.contents
+ running_on_gce = False
+ for (mkey, paths, required, is_text) in url_map:
+ value = None
+ for path in paths:
+ new_value = metadata_fetcher.get_value(path, is_text)
+ if new_value is not None:
+ value = new_value
+ if value:
+ running_on_gce = True
+ if required and value is None:
+ msg = "required key %s returned nothing. not GCE"
+ if not running_on_gce:
+ LOG.debug(msg, mkey)
else:
- if required:
- msg = "required url %s returned code %s. not GCE"
- if not found:
- LOG.debug(msg, path, resp.code)
- else:
- LOG.warn(msg, path, resp.code)
- return False
- else:
- self.metadata[mkey] = None
- except url_helper.UrlError as e:
- if required:
- msg = "required url %s raised exception %s. not GCE"
- if not found:
- LOG.debug(msg, path, e)
- else:
- LOG.warn(msg, path, e)
- return False
- msg = "Failed to get %s metadata item: %s."
- LOG.debug(msg, path, e)
-
- self.metadata[mkey] = None
+ LOG.warn(msg, mkey)
+ return False
+ self.metadata[mkey] = value
if self.metadata['public-keys']:
lines = self.metadata['public-keys'].splitlines()
self.metadata['public-keys'] = [self._trim_key(k) for k in lines]
+ if self.metadata['availability-zone']:
+ self.metadata['availability-zone'] = self.metadata[
+ 'availability-zone'].split('/')[-1]
+
encoding = self.metadata.get('user-data-encoding')
if encoding:
if encoding == 'base64':
@@ -113,7 +128,7 @@ class DataSourceGCE(sources.DataSource):
else:
LOG.warn('unknown user-data-encoding: %s, ignoring', encoding)
- return found
+ return running_on_gce
@property
def launch_index(self):
@@ -126,7 +141,7 @@ class DataSourceGCE(sources.DataSource):
def get_public_ssh_keys(self):
return self.metadata['public-keys']
- def get_hostname(self, fqdn=False, _resolve_ip=False):
+ def get_hostname(self, fqdn=False, resolve_ip=False):
# GCE has long FDQN's and has asked for short hostnames
return self.metadata['local-hostname'].split('.')[0]
@@ -137,6 +152,10 @@ class DataSourceGCE(sources.DataSource):
def availability_zone(self):
return self.metadata['availability-zone']
+ @property
+ def region(self):
+ return self.availability_zone.rsplit('-', 1)[0]
+
# Used to match classes to dependencies
datasources = [
(DataSourceGCE, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
diff --git a/cloudinit/sources/DataSourceMAAS.py b/cloudinit/sources/DataSourceMAAS.py
index dfe90bc6..d828f078 100644
--- a/cloudinit/sources/DataSourceMAAS.py
+++ b/cloudinit/sources/DataSourceMAAS.py
@@ -18,12 +18,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from email.utils import parsedate
+from __future__ import print_function
+
import errno
-import oauth.oauth as oauth
import os
import time
-import urllib2
from cloudinit import log as logging
from cloudinit import sources
@@ -33,6 +32,8 @@ from cloudinit import util
LOG = logging.getLogger(__name__)
MD_VERSION = "2012-03-01"
+BINARY_FIELDS = ('user-data',)
+
class DataSourceMAAS(sources.DataSource):
"""
@@ -47,7 +48,20 @@ class DataSourceMAAS(sources.DataSource):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
self.base_url = None
self.seed_dir = os.path.join(paths.seed_dir, 'maas')
- self.oauth_clockskew = None
+ self.oauth_helper = self._get_helper()
+
+ def _get_helper(self):
+ mcfg = self.ds_cfg
+ # If we are missing token_key, token_secret or consumer_key
+ # then just do non-authed requests
+ for required in ('token_key', 'token_secret', 'consumer_key'):
+ if required not in mcfg:
+ return url_helper.OauthUrlHelper()
+
+ return url_helper.OauthUrlHelper(
+ consumer_key=mcfg['consumer_key'], token_key=mcfg['token_key'],
+ token_secret=mcfg['token_secret'],
+ consumer_secret=mcfg.get('consumer_secret'))
def __str__(self):
root = sources.DataSource.__str__(self)
@@ -74,14 +88,18 @@ class DataSourceMAAS(sources.DataSource):
return False
try:
+ # doing this here actually has a side affect of
+ # getting oauth time-fix in place. As no where else would
+ # retry by default, so even if we could fix the timestamp
+ # we would not.
if not self.wait_for_metadata_service(url):
return False
self.base_url = url
- (userdata, metadata) = read_maas_seed_url(self.base_url,
- self._md_headers,
- paths=self.paths)
+ (userdata, metadata) = read_maas_seed_url(
+ self.base_url, read_file_or_url=self.oauth_helper.readurl,
+ paths=self.paths, retries=1)
self.userdata_raw = userdata
self.metadata = metadata
return True
@@ -89,31 +107,8 @@ class DataSourceMAAS(sources.DataSource):
util.logexc(LOG, "Failed fetching metadata from url %s", url)
return False
- def _md_headers(self, url):
- mcfg = self.ds_cfg
-
- # If we are missing token_key, token_secret or consumer_key
- # then just do non-authed requests
- for required in ('token_key', 'token_secret', 'consumer_key'):
- if required not in mcfg:
- return {}
-
- consumer_secret = mcfg.get('consumer_secret', "")
-
- timestamp = None
- if self.oauth_clockskew:
- timestamp = int(time.time()) + self.oauth_clockskew
-
- return oauth_headers(url=url,
- consumer_key=mcfg['consumer_key'],
- token_key=mcfg['token_key'],
- token_secret=mcfg['token_secret'],
- consumer_secret=consumer_secret,
- timestamp=timestamp)
-
def wait_for_metadata_service(self, url):
mcfg = self.ds_cfg
-
max_wait = 120
try:
max_wait = int(mcfg.get("max_wait", max_wait))
@@ -133,10 +128,8 @@ class DataSourceMAAS(sources.DataSource):
starttime = time.time()
check_url = "%s/%s/meta-data/instance-id" % (url, MD_VERSION)
urls = [check_url]
- url = url_helper.wait_for_url(urls=urls, max_wait=max_wait,
- timeout=timeout,
- exception_cb=self._except_cb,
- headers_cb=self._md_headers)
+ url = self.oauth_helper.wait_for_url(
+ urls=urls, max_wait=max_wait, timeout=timeout)
if url:
LOG.debug("Using metadata source: '%s'", url)
@@ -146,26 +139,6 @@ class DataSourceMAAS(sources.DataSource):
return bool(url)
- def _except_cb(self, msg, exception):
- if not (isinstance(exception, url_helper.UrlError) and
- (exception.code == 403 or exception.code == 401)):
- return
-
- if 'date' not in exception.headers:
- LOG.warn("Missing header 'date' in %s response", exception.code)
- return
-
- date = exception.headers['date']
- try:
- ret_time = time.mktime(parsedate(date))
- except Exception as e:
- LOG.warn("Failed to convert datetime '%s': %s", date, e)
- return
-
- self.oauth_clockskew = int(ret_time - time.time())
- LOG.warn("Setting oauth clockskew to %d", self.oauth_clockskew)
- return
-
def read_maas_seed_dir(seed_d):
"""
@@ -182,7 +155,8 @@ def read_maas_seed_dir(seed_d):
md = {}
for fname in files:
try:
- md[fname] = util.load_file(os.path.join(seed_d, fname))
+ md[fname] = util.load_file(os.path.join(seed_d, fname),
+ decode=fname not in BINARY_FIELDS)
except IOError as e:
if e.errno != errno.ENOENT:
raise
@@ -190,12 +164,12 @@ def read_maas_seed_dir(seed_d):
return check_seed_contents(md, seed_d)
-def read_maas_seed_url(seed_url, header_cb=None, timeout=None,
- version=MD_VERSION, paths=None):
+def read_maas_seed_url(seed_url, read_file_or_url=None, timeout=None,
+ version=MD_VERSION, paths=None, retries=None):
"""
Read the maas datasource at seed_url.
- - header_cb is a method that should return a headers dictionary for
- a given url
+ read_file_or_url is a method that should provide an interface
+ like util.read_file_or_url
Expected format of seed_url is are the following files:
* <seed_url>/<version>/meta-data/instance-id
@@ -215,27 +189,27 @@ def read_maas_seed_url(seed_url, header_cb=None, timeout=None,
'public-keys': "%s/%s" % (base_url, 'meta-data/public-keys'),
'user-data': "%s/%s" % (base_url, 'user-data'),
}
+
+ if read_file_or_url is None:
+ read_file_or_url = util.read_file_or_url
+
md = {}
for name in file_order:
url = files.get(name)
- if not header_cb:
- def _cb(url):
- return {}
- header_cb = _cb
-
if name == 'user-data':
- retries = 0
+ item_retries = 0
else:
- retries = None
+ item_retries = retries
try:
ssl_details = util.fetch_ssl_details(paths)
- resp = util.read_file_or_url(url, retries=retries,
- headers_cb=header_cb,
- timeout=timeout,
- ssl_details=ssl_details)
+ resp = read_file_or_url(url, retries=item_retries,
+ timeout=timeout, ssl_details=ssl_details)
if resp.ok():
- md[name] = str(resp)
+ if name in BINARY_FIELDS:
+ md[name] = resp.contents
+ else:
+ md[name] = util.decode_binary(resp.contents)
else:
LOG.warn(("Fetching from %s resulted in"
" an invalid http code %s"), url, resp.code)
@@ -260,9 +234,9 @@ def check_seed_contents(content, seed):
if len(missing):
raise MAASSeedDirMalformed("%s: missing files %s" % (seed, missing))
- userdata = content.get('user-data', "")
+ userdata = content.get('user-data', b"")
md = {}
- for (key, val) in content.iteritems():
+ for (key, val) in content.items():
if key == 'user-data':
continue
md[key] = val
@@ -270,29 +244,6 @@ def check_seed_contents(content, seed):
return (userdata, md)
-def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
- timestamp=None):
- consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
- token = oauth.OAuthToken(token_key, token_secret)
-
- if timestamp is None:
- ts = int(time.time())
- else:
- ts = timestamp
-
- params = {
- 'oauth_version': "1.0",
- 'oauth_nonce': oauth.generate_nonce(),
- 'oauth_timestamp': ts,
- 'oauth_token': token.key,
- 'oauth_consumer_key': consumer.key,
- }
- req = oauth.OAuthRequest(http_url=url, parameters=params)
- req.sign_request(oauth.OAuthSignatureMethod_PLAINTEXT(),
- consumer, token)
- return req.to_header()
-
-
class MAASSeedDirNone(Exception):
pass
@@ -303,7 +254,7 @@ class MAASSeedDirMalformed(Exception):
# Used to match classes to dependencies
datasources = [
- (DataSourceMAAS, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceMAAS, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
]
@@ -324,17 +275,18 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Interact with MAAS DS')
parser.add_argument("--config", metavar="file",
- help="specify DS config file", default=None)
+ help="specify DS config file", default=None)
parser.add_argument("--ckey", metavar="key",
- help="the consumer key to auth with", default=None)
+ help="the consumer key to auth with", default=None)
parser.add_argument("--tkey", metavar="key",
- help="the token key to auth with", default=None)
+ help="the token key to auth with", default=None)
parser.add_argument("--csec", metavar="secret",
- help="the consumer secret (likely '')", default="")
+ help="the consumer secret (likely '')", default="")
parser.add_argument("--tsec", metavar="secret",
- help="the token secret to auth with", default=None)
+ help="the token secret to auth with", default=None)
parser.add_argument("--apiver", metavar="version",
- help="the apiver to use ("" can be used)", default=MD_VERSION)
+ help="the apiver to use ("" can be used)",
+ default=MD_VERSION)
subcmds = parser.add_subparsers(title="subcommands", dest="subcmd")
subcmds.add_parser('crawl', help="crawl the datasource")
@@ -346,7 +298,7 @@ if __name__ == "__main__":
args = parser.parse_args()
creds = {'consumer_key': args.ckey, 'token_key': args.tkey,
- 'token_secret': args.tsec, 'consumer_secret': args.csec}
+ 'token_secret': args.tsec, 'consumer_secret': args.csec}
if args.config:
cfg = util.read_conf(args.config)
@@ -356,47 +308,46 @@ if __name__ == "__main__":
if key in cfg and creds[key] is None:
creds[key] = cfg[key]
- def geturl(url, headers_cb):
- req = urllib2.Request(url, data=None, headers=headers_cb(url))
- return (urllib2.urlopen(req).read())
+ oauth_helper = url_helper.OauthUrlHelper(**creds)
+
+ def geturl(url):
+ # the retry is to ensure that oauth timestamp gets fixed
+ return oauth_helper.readurl(url, retries=1).contents
- def printurl(url, headers_cb):
- print "== %s ==\n%s\n" % (url, geturl(url, headers_cb))
+ def printurl(url):
+ print("== %s ==\n%s\n" % (url, geturl(url).decode()))
- def crawl(url, headers_cb=None):
+ def crawl(url):
if url.endswith("/"):
- for line in geturl(url, headers_cb).splitlines():
+ for line in geturl(url).decode().splitlines():
if line.endswith("/"):
- crawl("%s%s" % (url, line), headers_cb)
+ crawl("%s%s" % (url, line))
+ elif line == "meta-data":
+ # meta-data is a dir, it *should* end in a /
+ crawl("%s%s" % (url, "meta-data/"))
else:
- printurl("%s%s" % (url, line), headers_cb)
+ printurl("%s%s" % (url, line))
else:
- printurl(url, headers_cb)
-
- def my_headers(url):
- headers = {}
- if creds.get('consumer_key', None) is not None:
- headers = oauth_headers(url, **creds)
- return headers
+ printurl(url)
if args.subcmd == "check-seed":
- if args.url.startswith("http"):
- (userdata, metadata) = read_maas_seed_url(args.url,
- header_cb=my_headers,
- version=args.apiver)
- else:
- (userdata, metadata) = read_maas_seed_url(args.url)
- print "=== userdata ==="
- print userdata
- print "=== metadata ==="
+ readurl = oauth_helper.readurl
+ if args.url[0] == "/" or args.url.startswith("file://"):
+ readurl = None
+ (userdata, metadata) = read_maas_seed_url(
+ args.url, version=args.apiver, read_file_or_url=readurl,
+ retries=2)
+ print("=== userdata ===")
+ print(userdata.decode())
+ print("=== metadata ===")
pprint.pprint(metadata)
elif args.subcmd == "get":
- printurl(args.url, my_headers)
+ printurl(args.url)
elif args.subcmd == "crawl":
if not args.url.endswith("/"):
args.url = "%s/" % args.url
- crawl(args.url, my_headers)
+ crawl(args.url)
main()
diff --git a/cloudinit/sources/DataSourceNoCloud.py b/cloudinit/sources/DataSourceNoCloud.py
index c26a645c..c2fba4d2 100644
--- a/cloudinit/sources/DataSourceNoCloud.py
+++ b/cloudinit/sources/DataSourceNoCloud.py
@@ -36,7 +36,9 @@ class DataSourceNoCloud(sources.DataSource):
self.dsmode = 'local'
self.seed = None
self.cmdline_id = "ds=nocloud"
- self.seed_dir = os.path.join(paths.seed_dir, 'nocloud')
+ self.seed_dirs = [os.path.join(paths.seed_dir, 'nocloud'),
+ os.path.join(paths.seed_dir, 'nocloud-net')]
+ self.seed_dir = None
self.supported_seed_starts = ("/", "file://")
def __str__(self):
@@ -50,31 +52,32 @@ class DataSourceNoCloud(sources.DataSource):
}
found = []
- mydata = {'meta-data': {}, 'user-data': "", 'vendor-data': ""}
+ mydata = {'meta-data': {}, 'user-data': "", 'vendor-data': "",
+ 'network-config': {}}
try:
# Parse the kernel command line, getting data passed in
md = {}
if parse_cmdline_data(self.cmdline_id, md):
found.append("cmdline")
- mydata['meta-data'].update(md)
+ mydata = _merge_new_seed(mydata, {'meta-data': md})
except:
util.logexc(LOG, "Unable to parse command line data")
return False
# Check to see if the seed dir has data.
pp2d_kwargs = {'required': ['user-data', 'meta-data'],
- 'optional': ['vendor-data']}
-
- try:
- seeded = util.pathprefix2dict(self.seed_dir, **pp2d_kwargs)
- found.append(self.seed_dir)
- LOG.debug("Using seeded data from %s", self.seed_dir)
- except ValueError as e:
- pass
-
- if self.seed_dir in found:
- mydata = _merge_new_seed(mydata, seeded)
+ 'optional': ['vendor-data', 'network-config']}
+
+ for path in self.seed_dirs:
+ try:
+ seeded = util.pathprefix2dict(path, **pp2d_kwargs)
+ found.append(path)
+ LOG.debug("Using seeded data from %s", path)
+ mydata = _merge_new_seed(mydata, seeded)
+ break
+ except ValueError as e:
+ pass
# If the datasource config had a 'seedfrom' entry, then that takes
# precedence over a 'seedfrom' that was found in a filesystem
@@ -124,7 +127,7 @@ class DataSourceNoCloud(sources.DataSource):
# that is more likely to be what is desired. If they want
# dsmode of local, then they must specify that.
if 'dsmode' not in mydata['meta-data']:
- mydata['dsmode'] = "net"
+ mydata['meta-data']['dsmode'] = "net"
LOG.debug("Using data from %s", dev)
found.append(dev)
@@ -141,8 +144,7 @@ class DataSourceNoCloud(sources.DataSource):
if len(found) == 0:
return False
- seeded_interfaces = None
-
+ seeded_network = None
# The special argument "seedfrom" indicates we should
# attempt to seed the userdata / metadata from its value
# its primarily value is in allowing the user to type less
@@ -158,8 +160,9 @@ class DataSourceNoCloud(sources.DataSource):
LOG.debug("Seed from %s not supported by %s", seedfrom, self)
return False
- if 'network-interfaces' in mydata['meta-data']:
- seeded_interfaces = self.dsmode
+ if (mydata['meta-data'].get('network-interfaces') or
+ mydata.get('network-config')):
+ seeded_network = self.dsmode
# This could throw errors, but the user told us to do it
# so if errors are raised, let them raise
@@ -176,26 +179,75 @@ class DataSourceNoCloud(sources.DataSource):
mydata['meta-data'] = util.mergemanydict([mydata['meta-data'],
defaults])
- # Update the network-interfaces if metadata had 'network-interfaces'
- # entry and this is the local datasource, or 'seedfrom' was used
- # and the source of the seed was self.dsmode
- # ('local' for NoCloud, 'net' for NoCloudNet')
- if ('network-interfaces' in mydata['meta-data'] and
- (self.dsmode in ("local", seeded_interfaces))):
- LOG.debug("Updating network interfaces from %s", self)
- self.distro.apply_network(
- mydata['meta-data']['network-interfaces'])
+ netdata = {'format': None, 'data': None}
+ if mydata['meta-data'].get('network-interfaces'):
+ netdata['format'] = 'interfaces'
+ netdata['data'] = mydata['meta-data']['network-interfaces']
+ elif mydata.get('network-config'):
+ netdata['format'] = 'network-config'
+ netdata['data'] = mydata['network-config']
+
+ # if this is the local datasource or 'seedfrom' was used
+ # and the source of the seed was self.dsmode.
+ # Then see if there is network config to apply.
+ # note this is obsolete network-interfaces style seeding.
+ if self.dsmode in ("local", seeded_network):
+ if mydata['meta-data'].get('network-interfaces'):
+ LOG.debug("Updating network interfaces from %s", self)
+ self.distro.apply_network(
+ mydata['meta-data']['network-interfaces'])
if mydata['meta-data']['dsmode'] == self.dsmode:
self.seed = ",".join(found)
self.metadata = mydata['meta-data']
self.userdata_raw = mydata['user-data']
- self.vendordata = mydata['vendor-data']
+ self.vendordata_raw = mydata['vendor-data']
+ self._network_config = mydata['network-config']
return True
- LOG.debug("%s: not claiming datasource, dsmode=%s", self, md['dsmode'])
+ LOG.debug("%s: not claiming datasource, dsmode=%s", self,
+ mydata['meta-data']['dsmode'])
return False
+ def check_instance_id(self, sys_cfg):
+ # quickly (local check only) if self.instance_id is still valid
+ # we check kernel command line or files.
+ current = self.get_instance_id()
+ if not current:
+ return None
+
+ quick_id = _quick_read_instance_id(cmdline_id=self.cmdline_id,
+ dirs=self.seed_dirs)
+ if not quick_id:
+ return None
+ return quick_id == current
+
+ @property
+ def network_config(self):
+ return self._network_config
+
+
+def _quick_read_instance_id(cmdline_id, dirs=None):
+ if dirs is None:
+ dirs = []
+
+ iid_key = 'instance-id'
+ if cmdline_id is None:
+ fill = {}
+ if parse_cmdline_data(cmdline_id, fill) and iid_key in fill:
+ return fill[iid_key]
+
+ for d in dirs:
+ try:
+ data = util.pathprefix2dict(d, required=['meta-data'])
+ md = util.load_yaml(data['meta-data'])
+ if iid_key in md:
+ return md[iid_key]
+ except ValueError:
+ pass
+
+ return None
+
# Returns true or false indicating if cmdline indicated
# that this module should be used
@@ -243,9 +295,17 @@ def parse_cmdline_data(ds_id, fill, cmdline=None):
def _merge_new_seed(cur, seeded):
ret = cur.copy()
- ret['meta-data'] = util.mergemanydict([cur['meta-data'],
- util.load_yaml(seeded['meta-data'])])
- ret['user-data'] = seeded['user-data']
+
+ newmd = seeded.get('meta-data', {})
+ if not isinstance(seeded['meta-data'], dict):
+ newmd = util.load_yaml(seeded['meta-data'])
+ ret['meta-data'] = util.mergemanydict([cur['meta-data'], newmd])
+
+ if seeded.get('network-config'):
+ ret['network-config'] = util.load_yaml(seeded['network-config'])
+
+ if 'user-data' in seeded:
+ ret['user-data'] = seeded['user-data']
if 'vendor-data' in seeded:
ret['vendor-data'] = seeded['vendor-data']
return ret
@@ -256,14 +316,13 @@ class DataSourceNoCloudNet(DataSourceNoCloud):
DataSourceNoCloud.__init__(self, sys_cfg, distro, paths)
self.cmdline_id = "ds=nocloud-net"
self.supported_seed_starts = ("http://", "https://", "ftp://")
- self.seed_dir = os.path.join(paths.seed_dir, 'nocloud-net')
self.dsmode = "net"
# Used to match classes to dependencies
datasources = [
- (DataSourceNoCloud, (sources.DEP_FILESYSTEM, )),
- (DataSourceNoCloudNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceNoCloud, (sources.DEP_FILESYSTEM, )),
+ (DataSourceNoCloudNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
]
diff --git a/cloudinit/sources/DataSourceNone.py b/cloudinit/sources/DataSourceNone.py
index 12a8a992..d1a62b2a 100644
--- a/cloudinit/sources/DataSourceNone.py
+++ b/cloudinit/sources/DataSourceNone.py
@@ -47,8 +47,8 @@ class DataSourceNone(sources.DataSource):
# Used to match classes to dependencies
datasources = [
- (DataSourceNone, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
- (DataSourceNone, []),
+ (DataSourceNone, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceNone, []),
]
diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py
index 7ba60735..2a6cd050 100644
--- a/cloudinit/sources/DataSourceOVF.py
+++ b/cloudinit/sources/DataSourceOVF.py
@@ -25,10 +25,22 @@ from xml.dom import minidom
import base64
import os
import re
+import time
from cloudinit import log as logging
from cloudinit import sources
from cloudinit import util
+from .helpers.vmware.imc.config import Config
+from .helpers.vmware.imc.config_file import ConfigFile
+from .helpers.vmware.imc.config_nic import NicConfigurator
+from .helpers.vmware.imc.guestcust_event import GuestCustEventEnum
+from .helpers.vmware.imc.guestcust_state import GuestCustStateEnum
+from .helpers.vmware.imc.guestcust_error import GuestCustErrorEnum
+from .helpers.vmware.imc.guestcust_util import (
+ set_customization_status,
+ get_nics_to_enable,
+ enable_nics
+)
LOG = logging.getLogger(__name__)
@@ -50,23 +62,95 @@ class DataSourceOVF(sources.DataSource):
found = []
md = {}
ud = ""
+ vmwarePlatformFound = False
+ vmwareImcConfigFilePath = ''
defaults = {
"instance-id": "iid-dsovf",
}
(seedfile, contents) = get_ovf_env(self.paths.seed_dir)
+
+ system_type = util.read_dmi_data("system-product-name")
+ if system_type is None:
+ LOG.debug("No system-product-name found")
+
if seedfile:
# Found a seed dir
seed = os.path.join(self.paths.seed_dir, seedfile)
(md, ud, cfg) = read_ovf_environment(contents)
self.environment = contents
found.append(seed)
+ elif system_type and 'vmware' in system_type.lower():
+ LOG.debug("VMware Virtualization Platform found")
+ if not util.get_cfg_option_bool(
+ self.sys_cfg, "disable_vmware_customization", True):
+ deployPkgPluginPath = search_file("/usr/lib/vmware-tools",
+ "libdeployPkgPlugin.so")
+ if not deployPkgPluginPath:
+ deployPkgPluginPath = search_file("/usr/lib/open-vm-tools",
+ "libdeployPkgPlugin.so")
+ if deployPkgPluginPath:
+ # When the VM is powered on, the "VMware Tools" daemon
+ # copies the customization specification file to
+ # /var/run/vmware-imc directory. cloud-init code needs
+ # to search for the file in that directory.
+ vmwareImcConfigFilePath = util.log_time(
+ logfunc=LOG.debug,
+ msg="waiting for configuration file",
+ func=wait_for_imc_cfg_file,
+ args=("/var/run/vmware-imc", "cust.cfg"))
+
+ if vmwareImcConfigFilePath:
+ LOG.debug("Found VMware DeployPkg Config File at %s" %
+ vmwareImcConfigFilePath)
+ else:
+ LOG.debug("Did not find VMware DeployPkg Config File Path")
+ else:
+ LOG.debug("Customization for VMware platform is disabled.")
+
+ if vmwareImcConfigFilePath:
+ nics = ""
+ try:
+ cf = ConfigFile(vmwareImcConfigFilePath)
+ conf = Config(cf)
+ (md, ud, cfg) = read_vmware_imc(conf)
+ dirpath = os.path.dirname(vmwareImcConfigFilePath)
+ nics = get_nics_to_enable(dirpath)
+ except Exception as e:
+ LOG.debug("Error parsing the customization Config File")
+ LOG.exception(e)
+ set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
+ GuestCustEventEnum.GUESTCUST_EVENT_CUSTOMIZE_FAILED)
+ enable_nics(nics)
+ return False
+ finally:
+ util.del_dir(os.path.dirname(vmwareImcConfigFilePath))
+
+ try:
+ LOG.debug("Applying the Network customization")
+ nicConfigurator = NicConfigurator(conf.nics)
+ nicConfigurator.configure()
+ except Exception as e:
+ LOG.debug("Error applying the Network Configuration")
+ LOG.exception(e)
+ set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
+ GuestCustEventEnum.GUESTCUST_EVENT_NETWORK_SETUP_FAILED)
+ enable_nics(nics)
+ return False
+
+ vmwarePlatformFound = True
+ set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_DONE,
+ GuestCustErrorEnum.GUESTCUST_ERROR_SUCCESS)
+ enable_nics(nics)
else:
np = {'iso': transport_iso9660,
'vmware-guestd': transport_vmware_guestd, }
name = None
- for (name, transfunc) in np.iteritems():
+ for (name, transfunc) in np.items():
(contents, _dev, _fname) = transfunc()
if contents:
break
@@ -76,7 +160,7 @@ class DataSourceOVF(sources.DataSource):
found.append(name)
# There was no OVF transports found
- if len(found) == 0:
+ if len(found) == 0 and not vmwarePlatformFound:
return False
if 'seedfrom' in md and md['seedfrom']:
@@ -129,6 +213,36 @@ class DataSourceOVFNet(DataSourceOVF):
self.supported_seed_starts = ("http://", "https://", "ftp://")
+def wait_for_imc_cfg_file(dirpath, filename, maxwait=180, naplen=5):
+ waited = 0
+
+ while waited < maxwait:
+ fileFullPath = search_file(dirpath, filename)
+ if fileFullPath:
+ return fileFullPath
+ time.sleep(naplen)
+ waited += naplen
+ return None
+
+
+# This will return a dict with some content
+# meta-data, user-data, some config
+def read_vmware_imc(config):
+ md = {}
+ cfg = {}
+ ud = ""
+ if config.host_name:
+ if config.domain_name:
+ md['local-hostname'] = config.host_name + "." + config.domain_name
+ else:
+ md['local-hostname'] = config.host_name
+
+ if config.timezone:
+ cfg['timezone'] = config.timezone
+
+ return (md, ud, cfg)
+
+
# This will return a dict with some content
# meta-data, user-data, some config
def read_ovf_environment(contents):
@@ -138,7 +252,7 @@ def read_ovf_environment(contents):
ud = ""
cfg_props = ['password']
md_props = ['seedfrom', 'local-hostname', 'public-keys', 'instance-id']
- for (prop, val) in props.iteritems():
+ for (prop, val) in props.items():
if prop == 'hostname':
prop = "local-hostname"
if prop in md_props:
@@ -183,7 +297,7 @@ def transport_iso9660(require_iso=True):
# Go through mounts to see if it was already mounted
mounts = util.mounts()
- for (dev, info) in mounts.iteritems():
+ for (dev, info) in mounts.items():
fstype = info['fstype']
if fstype != "iso9660" and require_iso:
continue
@@ -264,14 +378,14 @@ def get_properties(contents):
# could also check here that elem.namespaceURI ==
# "http://schemas.dmtf.org/ovf/environment/1"
propSections = find_child(dom.documentElement,
- lambda n: n.localName == "PropertySection")
+ lambda n: n.localName == "PropertySection")
if len(propSections) == 0:
raise XmlError("No 'PropertySection's")
props = {}
propElems = find_child(propSections[0],
- (lambda n: n.localName == "Property"))
+ (lambda n: n.localName == "Property"))
for elem in propElems:
key = elem.attributes.getNamedItemNS(envNsURI, "key").value
@@ -281,14 +395,25 @@ def get_properties(contents):
return props
+def search_file(dirpath, filename):
+ if not dirpath or not filename:
+ return None
+
+ for root, dirs, files in os.walk(dirpath):
+ if filename in files:
+ return os.path.join(root, filename)
+
+ return None
+
+
class XmlError(Exception):
pass
# Used to match classes to dependencies
datasources = (
- (DataSourceOVF, (sources.DEP_FILESYSTEM, )),
- (DataSourceOVFNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+ (DataSourceOVF, (sources.DEP_FILESYSTEM, )),
+ (DataSourceOVFNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
)
diff --git a/cloudinit/sources/DataSourceOpenNebula.py b/cloudinit/sources/DataSourceOpenNebula.py
index e2469f6e..681f3a96 100644
--- a/cloudinit/sources/DataSourceOpenNebula.py
+++ b/cloudinit/sources/DataSourceOpenNebula.py
@@ -24,7 +24,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import base64
import os
import pwd
import re
@@ -34,6 +33,7 @@ from cloudinit import log as logging
from cloudinit import sources
from cloudinit import util
+
LOG = logging.getLogger(__name__)
DEFAULT_IID = "iid-dsopennebula"
@@ -149,8 +149,8 @@ class BrokenContextDiskDir(Exception):
class OpenNebulaNetwork(object):
REG_DEV_MAC = re.compile(
- r'^\d+: (eth\d+):.*?link\/ether (..:..:..:..:..:..) ?',
- re.MULTILINE | re.DOTALL)
+ r'^\d+: (eth\d+):.*?link\/ether (..:..:..:..:..:..) ?',
+ re.MULTILINE | re.DOTALL)
def __init__(self, ip, context):
self.ip = ip
@@ -280,7 +280,7 @@ def parse_shell_config(content, keylist=None, bash=None, asuser=None,
# allvars expands to all existing variables by using '${!x*}' notation
# where x is lower or upper case letters or '_'
- allvars = ["${!%s*}" % x for x in string.letters + "_"]
+ allvars = ["${!%s*}" % x for x in string.ascii_letters + "_"]
keylist_in = keylist
if keylist is None:
@@ -379,9 +379,8 @@ def read_context_disk_dir(source_dir, asuser=None):
raise BrokenContextDiskDir("configured user '%s' "
"does not exist", asuser)
try:
- with open(os.path.join(source_dir, 'context.sh'), 'r') as f:
- content = f.read().strip()
-
+ path = os.path.join(source_dir, 'context.sh')
+ content = util.load_file(path)
context = parse_shell_config(content, asuser=asuser)
except util.ProcessExecutionError as e:
raise BrokenContextDiskDir("Error processing context.sh: %s" % (e))
@@ -405,7 +404,8 @@ def read_context_disk_dir(source_dir, asuser=None):
if ssh_key_var:
lines = context.get(ssh_key_var).splitlines()
results['metadata']['public-keys'] = [l for l in lines
- if len(l) and not l.startswith("#")]
+ if len(l) and not
+ l.startswith("#")]
# custom hostname -- try hostname or leave cloud-init
# itself create hostname from IP address later
@@ -426,14 +426,14 @@ def read_context_disk_dir(source_dir, asuser=None):
context.get('USER_DATA_ENCODING'))
if encoding == "base64":
try:
- results['userdata'] = base64.b64decode(results['userdata'])
+ results['userdata'] = util.b64d(results['userdata'])
except TypeError:
LOG.warn("Failed base64 decoding of userdata")
# generate static /etc/network/interfaces
# only if there are any required context variables
# http://opennebula.org/documentation:rel3.8:cong#network_configuration
- for k in context.keys():
+ for k in context:
if re.match(r'^ETH\d+_IP$', k):
(out, _) = util.subp(['/sbin/ip', 'link'])
net = OpenNebulaNetwork(out, context)
diff --git a/cloudinit/sources/DataSourceOpenStack.py b/cloudinit/sources/DataSourceOpenStack.py
index 469c2e2a..f7f4590b 100644
--- a/cloudinit/sources/DataSourceOpenStack.py
+++ b/cloudinit/sources/DataSourceOpenStack.py
@@ -150,6 +150,10 @@ class DataSourceOpenStack(openstack.SourceMixin, sources.DataSource):
return True
+ def check_instance_id(self, sys_cfg):
+ # quickly (local check only) if self.instance_id is still valid
+ return sources.instance_id_matches_system_uuid(self.get_instance_id())
+
def read_metadata_service(base_url, ssl_details=None):
reader = openstack.MetadataReader(base_url, ssl_details=ssl_details)
diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py
index 2733a2f6..5edab152 100644
--- a/cloudinit/sources/DataSourceSmartOS.py
+++ b/cloudinit/sources/DataSourceSmartOS.py
@@ -20,28 +20,38 @@
# Datasource for provisioning on SmartOS. This works on Joyent
# and public/private Clouds using SmartOS.
#
-# SmartOS hosts use a serial console (/dev/ttyS1) on Linux Guests.
+# SmartOS hosts use a serial console (/dev/ttyS1) on KVM Linux Guests
# The meta-data is transmitted via key/value pairs made by
# requests on the console. For example, to get the hostname, you
# would send "GET hostname" on /dev/ttyS1.
+# For Linux Guests running in LX-Brand Zones on SmartOS hosts
+# a socket (/native/.zonecontrol/metadata.sock) is used instead
+# of a serial console.
#
# Certain behavior is defined by the DataDictionary
# http://us-east.manta.joyent.com/jmc/public/mdata/datadict.html
# Comments with "@datadictionary" are snippets of the definition
-import base64
+import binascii
+import contextlib
+import os
+import random
+import re
+import socket
+import stat
+
+import serial
+
from cloudinit import log as logging
from cloudinit import sources
from cloudinit import util
-import os
-import os.path
-import serial
LOG = logging.getLogger(__name__)
SMARTOS_ATTRIB_MAP = {
# Cloud-init Key : (SmartOS Key, Strip line endings)
+ 'instance-id': ('sdc:uuid', True),
'local-hostname': ('hostname', True),
'public-keys': ('root_authorized_keys', True),
'user-script': ('user-script', False),
@@ -72,6 +82,7 @@ DS_CFG_PATH = ['datasource', DS_NAME]
#
BUILTIN_DS_CONFIG = {
'serial_device': '/dev/ttyS1',
+ 'metadata_sockfile': '/native/.zonecontrol/metadata.sock',
'seed_timeout': 60,
'no_base64_decode': ['root_authorized_keys',
'motd_sys_info',
@@ -79,7 +90,7 @@ BUILTIN_DS_CONFIG = {
'user-data',
'user-script',
'sdc:datacenter_name',
- ],
+ 'sdc:uuid'],
'base64_keys': [],
'base64_all': False,
'disk_aliases': {'ephemeral0': '/dev/vdb'},
@@ -90,7 +101,7 @@ BUILTIN_CLOUD_CONFIG = {
'ephemeral0': {'table_type': 'mbr',
'layout': False,
'overwrite': False}
- },
+ },
'fs_setup': [{'label': 'ephemeral0',
'filesystem': 'ext3',
'device': 'ephemeral0'}],
@@ -146,17 +157,27 @@ class DataSourceSmartOS(sources.DataSource):
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
self.is_smartdc = None
-
self.ds_cfg = util.mergemanydict([
self.ds_cfg,
util.get_cfg_by_path(sys_cfg, DS_CFG_PATH, {}),
BUILTIN_DS_CONFIG])
self.metadata = {}
- self.cfg = BUILTIN_CLOUD_CONFIG
- self.seed = self.ds_cfg.get("serial_device")
- self.seed_timeout = self.ds_cfg.get("serial_timeout")
+ # SDC LX-Brand Zones lack dmidecode (no /dev/mem) but
+ # report 'BrandZ virtual linux' as the kernel version
+ if os.uname()[3].lower() == 'brandz virtual linux':
+ LOG.debug("Host is SmartOS, guest in Zone")
+ self.is_smartdc = True
+ self.smartos_type = 'lx-brand'
+ self.cfg = {}
+ self.seed = self.ds_cfg.get("metadata_sockfile")
+ else:
+ self.is_smartdc = True
+ self.smartos_type = 'kvm'
+ self.seed = self.ds_cfg.get("serial_device")
+ self.cfg = BUILTIN_CLOUD_CONFIG
+ self.seed_timeout = self.ds_cfg.get("serial_timeout")
self.smartos_no_base64 = self.ds_cfg.get('no_base64_decode')
self.b64_keys = self.ds_cfg.get('base64_keys')
self.b64_all = self.ds_cfg.get('base64_all')
@@ -166,12 +187,49 @@ class DataSourceSmartOS(sources.DataSource):
root = sources.DataSource.__str__(self)
return "%s [seed=%s]" % (root, self.seed)
+ def _get_seed_file_object(self):
+ if not self.seed:
+ raise AttributeError("seed device is not set")
+
+ if self.smartos_type == 'lx-brand':
+ if not stat.S_ISSOCK(os.stat(self.seed).st_mode):
+ LOG.debug("Seed %s is not a socket", self.seed)
+ return None
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(self.seed)
+ return sock.makefile('rwb')
+ else:
+ if not stat.S_ISCHR(os.stat(self.seed).st_mode):
+ LOG.debug("Seed %s is not a character device")
+ return None
+ ser = serial.Serial(self.seed, timeout=self.seed_timeout)
+ if not ser.isOpen():
+ raise SystemError("Unable to open %s" % self.seed)
+ return ser
+ return None
+
+ def _set_provisioned(self):
+ '''Mark the instance provisioning state as successful.
+
+ When run in a zone, the host OS will look for /var/svc/provisioning
+ to be renamed as /var/svc/provision_success. This should be done
+ after meta-data is successfully retrieved and from this point
+ the host considers the provision of the zone to be a success and
+ keeps the zone running.
+ '''
+
+ LOG.debug('Instance provisioning state set as successful')
+ svc_path = '/var/svc'
+ if os.path.exists('/'.join([svc_path, 'provisioning'])):
+ os.rename('/'.join([svc_path, 'provisioning']),
+ '/'.join([svc_path, 'provision_success']))
+
def get_data(self):
md = {}
ud = ""
if not device_exists(self.seed):
- LOG.debug("No serial device '%s' found for SmartOS datasource",
+ LOG.debug("No metadata device '%s' found for SmartOS datasource",
self.seed)
return False
@@ -181,29 +239,36 @@ class DataSourceSmartOS(sources.DataSource):
LOG.debug("Disabling SmartOS datasource on arm (LP: #1243287)")
return False
- dmi_info = dmi_data()
- if dmi_info is False:
- LOG.debug("No dmidata utility found")
- return False
-
- system_uuid, system_type = tuple(dmi_info)
- if 'smartdc' not in system_type.lower():
- LOG.debug("Host is not on SmartOS. system_type=%s", system_type)
+ # SDC KVM instances will provide dmi data, LX-brand does not
+ if self.smartos_type == 'kvm':
+ dmi_info = dmi_data()
+ if dmi_info is False:
+ LOG.debug("No dmidata utility found")
+ return False
+
+ system_type = dmi_info
+ if 'smartdc' not in system_type.lower():
+ LOG.debug("Host is not on SmartOS. system_type=%s",
+ system_type)
+ return False
+ LOG.debug("Host is SmartOS, guest in KVM")
+
+ seed_obj = self._get_seed_file_object()
+ if seed_obj is None:
+ LOG.debug('Seed file object not found.')
return False
- self.is_smartdc = True
- md['instance-id'] = system_uuid
-
- b64_keys = self.query('base64_keys', strip=True, b64=False)
- if b64_keys is not None:
- self.b64_keys = [k.strip() for k in str(b64_keys).split(',')]
+ with contextlib.closing(seed_obj) as seed:
+ b64_keys = self.query('base64_keys', seed, strip=True, b64=False)
+ if b64_keys is not None:
+ self.b64_keys = [k.strip() for k in str(b64_keys).split(',')]
- b64_all = self.query('base64_all', strip=True, b64=False)
- if b64_all is not None:
- self.b64_all = util.is_true(b64_all)
+ b64_all = self.query('base64_all', seed, strip=True, b64=False)
+ if b64_all is not None:
+ self.b64_all = util.is_true(b64_all)
- for ci_noun, attribute in SMARTOS_ATTRIB_MAP.iteritems():
- smartos_noun, strip = attribute
- md[ci_noun] = self.query(smartos_noun, strip=strip)
+ for ci_noun, attribute in SMARTOS_ATTRIB_MAP.items():
+ smartos_noun, strip = attribute
+ md[ci_noun] = self.query(smartos_noun, seed, strip=strip)
# @datadictionary: This key may contain a program that is written
# to a file in the filesystem of the guest on each boot and then
@@ -218,11 +283,12 @@ class DataSourceSmartOS(sources.DataSource):
user_script = os.path.join(data_d, 'user-script')
u_script_l = "%s/user-script" % LEGACY_USER_D
write_boot_content(md.get('user-script'), content_f=user_script,
- link=u_script_l, shebang=True, mode=0700)
+ link=u_script_l, shebang=True, mode=0o700)
operator_script = os.path.join(data_d, 'operator-script')
write_boot_content(md.get('operator-script'),
- content_f=operator_script, shebang=False, mode=0700)
+ content_f=operator_script, shebang=False,
+ mode=0o700)
# @datadictionary: This key has no defined format, but its value
# is written to the file /var/db/mdata-user-data on each boot prior
@@ -235,7 +301,7 @@ class DataSourceSmartOS(sources.DataSource):
# Handle the cloud-init regular meta
if not md['local-hostname']:
- md['local-hostname'] = system_uuid
+ md['local-hostname'] = md['instance-id']
ud = None
if md['user-data']:
@@ -252,6 +318,8 @@ class DataSourceSmartOS(sources.DataSource):
self.metadata = util.mergemanydict([md, self.metadata])
self.userdata_raw = ud
self.vendordata_raw = md['vendor-data']
+
+ self._set_provisioned()
return True
def device_name_to_device(self, name):
@@ -263,125 +331,146 @@ class DataSourceSmartOS(sources.DataSource):
def get_instance_id(self):
return self.metadata['instance-id']
- def query(self, noun, strip=False, default=None, b64=None):
+ def query(self, noun, seed_file, strip=False, default=None, b64=None):
if b64 is None:
if noun in self.smartos_no_base64:
b64 = False
elif self.b64_all or noun in self.b64_keys:
b64 = True
- return query_data(noun=noun, strip=strip, seed_device=self.seed,
- seed_timeout=self.seed_timeout, default=default,
- b64=b64)
+ return self._query_data(noun, seed_file, strip=strip,
+ default=default, b64=b64)
+ def _query_data(self, noun, seed_file, strip=False,
+ default=None, b64=None):
+ """Makes a request via "GET <NOUN>"
-def device_exists(device):
- """Symplistic method to determine if the device exists or not"""
- return os.path.exists(device)
+ In the response, the first line is the status, while subsequent
+ lines are is the value. A blank line with a "." is used to
+ indicate end of response.
+ If the response is expected to be base64 encoded, then set
+ b64encoded to true. Unfortantely, there is no way to know if
+ something is 100% encoded, so this method relies on being told
+ if the data is base64 or not.
+ """
-def get_serial(seed_device, seed_timeout):
- """This is replaced in unit testing, allowing us to replace
- serial.Serial with a mocked class.
-
- The timeout value of 60 seconds should never be hit. The value
- is taken from SmartOS own provisioning tools. Since we are reading
- each line individually up until the single ".", the transfer is
- usually very fast (i.e. microseconds) to get the response.
- """
- if not seed_device:
- raise AttributeError("seed_device value is not set")
-
- ser = serial.Serial(seed_device, timeout=seed_timeout)
- if not ser.isOpen():
- raise SystemError("Unable to open %s" % seed_device)
-
- return ser
+ if not noun:
+ return False
+ response = JoyentMetadataClient(seed_file).get_metadata(noun)
-def query_data(noun, seed_device, seed_timeout, strip=False, default=None,
- b64=None):
- """Makes a request to via the serial console via "GET <NOUN>"
+ if response is None:
+ return default
- In the response, the first line is the status, while subsequent lines
- are is the value. A blank line with a "." is used to indicate end of
- response.
+ if b64 is None:
+ b64 = self._query_data('b64-%s' % noun, seed_file, b64=False,
+ default=False, strip=True)
+ b64 = util.is_true(b64)
- If the response is expected to be base64 encoded, then set b64encoded
- to true. Unfortantely, there is no way to know if something is 100%
- encoded, so this method relies on being told if the data is base64 or
- not.
- """
+ resp = None
+ if b64 or strip:
+ resp = "".join(response).rstrip()
+ else:
+ resp = "".join(response)
- if not noun:
- return False
+ if b64:
+ try:
+ return util.b64d(resp)
+ # Bogus input produces different errors in Python 2 and 3;
+ # catch both.
+ except (TypeError, binascii.Error):
+ LOG.warn("Failed base64 decoding key '%s'", noun)
+ return resp
- ser = get_serial(seed_device, seed_timeout)
- ser.write("GET %s\n" % noun.rstrip())
- status = str(ser.readline()).rstrip()
- response = []
- eom_found = False
+ return resp
- if 'SUCCESS' not in status:
- ser.close()
- return default
- while not eom_found:
- m = ser.readline()
- if m.rstrip() == ".":
- eom_found = True
- else:
- response.append(m)
+def device_exists(device):
+ """Symplistic method to determine if the device exists or not"""
+ return os.path.exists(device)
- ser.close()
- if b64 is None:
- b64 = query_data('b64-%s' % noun, seed_device=seed_device,
- seed_timeout=seed_timeout, b64=False,
- default=False, strip=True)
- b64 = util.is_true(b64)
+class JoyentMetadataFetchException(Exception):
+ pass
- resp = None
- if b64 or strip:
- resp = "".join(response).rstrip()
- else:
- resp = "".join(response)
- if b64:
- try:
- return base64.b64decode(resp)
- except TypeError:
- LOG.warn("Failed base64 decoding key '%s'", noun)
- return resp
+class JoyentMetadataClient(object):
+ """
+ A client implementing v2 of the Joyent Metadata Protocol Specification.
- return resp
+ The full specification can be found at
+ http://eng.joyent.com/mdata/protocol.html
+ """
+ line_regex = re.compile(
+ r'V2 (?P<length>\d+) (?P<checksum>[0-9a-f]+)'
+ r' (?P<body>(?P<request_id>[0-9a-f]+) (?P<status>SUCCESS|NOTFOUND)'
+ r'( (?P<payload>.+))?)')
+
+ def __init__(self, metasource):
+ self.metasource = metasource
+
+ def _checksum(self, body):
+ return '{0:08x}'.format(
+ binascii.crc32(body.encode('utf-8')) & 0xffffffff)
+
+ def _get_value_from_frame(self, expected_request_id, frame):
+ frame_data = self.line_regex.match(frame).groupdict()
+ if int(frame_data['length']) != len(frame_data['body']):
+ raise JoyentMetadataFetchException(
+ 'Incorrect frame length given ({0} != {1}).'.format(
+ frame_data['length'], len(frame_data['body'])))
+ expected_checksum = self._checksum(frame_data['body'])
+ if frame_data['checksum'] != expected_checksum:
+ raise JoyentMetadataFetchException(
+ 'Invalid checksum (expected: {0}; got {1}).'.format(
+ expected_checksum, frame_data['checksum']))
+ if frame_data['request_id'] != expected_request_id:
+ raise JoyentMetadataFetchException(
+ 'Request ID mismatch (expected: {0}; got {1}).'.format(
+ expected_request_id, frame_data['request_id']))
+ if not frame_data.get('payload', None):
+ LOG.debug('No value found.')
+ return None
+ value = util.b64d(frame_data['payload'])
+ LOG.debug('Value "%s" found.', value)
+ return value
+
+ def get_metadata(self, metadata_key):
+ LOG.debug('Fetching metadata key "%s"...', metadata_key)
+ request_id = '{0:08x}'.format(random.randint(0, 0xffffffff))
+ message_body = '{0} GET {1}'.format(request_id,
+ util.b64e(metadata_key))
+ msg = 'V2 {0} {1} {2}\n'.format(
+ len(message_body), self._checksum(message_body), message_body)
+ LOG.debug('Writing "%s" to metadata transport.', msg)
+ self.metasource.write(msg.encode('ascii'))
+ self.metasource.flush()
+
+ response = bytearray()
+ response.extend(self.metasource.read(1))
+ while response[-1:] != b'\n':
+ response.extend(self.metasource.read(1))
+ response = response.rstrip().decode('ascii')
+ LOG.debug('Read "%s" from metadata transport.', response)
+
+ if 'SUCCESS' not in response:
+ return None
+
+ return self._get_value_from_frame(request_id, response)
def dmi_data():
- sys_uuid, sys_type = None, None
- dmidecode_path = util.which('dmidecode')
- if not dmidecode_path:
- return False
-
- sys_uuid_cmd = [dmidecode_path, "-s", "system-uuid"]
- try:
- LOG.debug("Getting hostname from dmidecode")
- (sys_uuid, _err) = util.subp(sys_uuid_cmd)
- except Exception as e:
- util.logexc(LOG, "Failed to get system UUID", e)
+ sys_type = util.read_dmi_data("system-product-name")
- sys_type_cmd = [dmidecode_path, "-s", "system-product-name"]
- try:
- LOG.debug("Determining hypervisor product name via dmidecode")
- (sys_type, _err) = util.subp(sys_type_cmd)
- except Exception as e:
- util.logexc(LOG, "Failed to get system UUID", e)
+ if not sys_type:
+ return None
- return (sys_uuid.lower().strip(), sys_type.strip())
+ return sys_type
def write_boot_content(content, content_f, link=None, shebang=False,
- mode=0400):
+ mode=0o400):
"""
Write the content to content_f. Under the following rules:
1. If no content, remove the file
@@ -423,7 +512,7 @@ def write_boot_content(content, content_f, link=None, shebang=False,
except Exception as e:
util.logexc(LOG, ("Failed to identify script type for %s" %
- content_f, e))
+ content_f, e))
if link:
try:
diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py
index 7c7ef9ab..82cd3553 100644
--- a/cloudinit/sources/__init__.py
+++ b/cloudinit/sources/__init__.py
@@ -23,6 +23,8 @@
import abc
import os
+import six
+
from cloudinit import importer
from cloudinit import log as logging
from cloudinit import type_utils
@@ -30,6 +32,7 @@ from cloudinit import user_data as ud
from cloudinit import util
from cloudinit.filters import launch_index
+from cloudinit.reporting import events
DEP_FILESYSTEM = "FILESYSTEM"
DEP_NETWORK = "NETWORK"
@@ -130,7 +133,7 @@ class DataSource(object):
# we want to return the correct value for what will actually
# exist in this instance
mappings = {"sd": ("vd", "xvd", "vtb")}
- for (nfrom, tlist) in mappings.iteritems():
+ for (nfrom, tlist) in mappings.items():
if not short_name.startswith(nfrom):
continue
for nto in tlist:
@@ -155,6 +158,10 @@ class DataSource(object):
return self.metadata.get('availability-zone',
self.metadata.get('availability_zone'))
+ @property
+ def region(self):
+ return self.metadata.get('region')
+
def get_instance_id(self):
if not self.metadata or 'instance-id' not in self.metadata:
# Return a magic not really instance id string
@@ -208,8 +215,15 @@ class DataSource(object):
return hostname
def get_package_mirror_info(self):
- return self.distro.get_package_mirror_info(
- availability_zone=self.availability_zone)
+ return self.distro.get_package_mirror_info(data_source=self)
+
+ def check_instance_id(self, sys_cfg):
+ # quickly (local check only) if self.instance_id is still
+ return False
+
+ @property
+ def network_config(self):
+ return None
def normalize_pubkey_data(pubkey_data):
@@ -218,18 +232,18 @@ def normalize_pubkey_data(pubkey_data):
if not pubkey_data:
return keys
- if isinstance(pubkey_data, (basestring, str)):
+ if isinstance(pubkey_data, six.string_types):
return str(pubkey_data).splitlines()
if isinstance(pubkey_data, (list, set)):
return list(pubkey_data)
if isinstance(pubkey_data, (dict)):
- for (_keyname, klist) in pubkey_data.iteritems():
+ for (_keyname, klist) in pubkey_data.items():
# lp:506332 uec metadata service responds with
# data that makes boto populate a string for 'klist' rather
# than a list.
- if isinstance(klist, (str, basestring)):
+ if isinstance(klist, six.string_types):
klist = [klist]
if isinstance(klist, (list, set)):
for pkey in klist:
@@ -241,17 +255,25 @@ def normalize_pubkey_data(pubkey_data):
return keys
-def find_source(sys_cfg, distro, paths, ds_deps, cfg_list, pkg_list):
+def find_source(sys_cfg, distro, paths, ds_deps, cfg_list, pkg_list, reporter):
ds_list = list_sources(cfg_list, ds_deps, pkg_list)
ds_names = [type_utils.obj_name(f) for f in ds_list]
- LOG.debug("Searching for data source in: %s", ds_names)
-
- for cls in ds_list:
+ mode = "network" if DEP_NETWORK in ds_deps else "local"
+ LOG.debug("Searching for %s data source in: %s", mode, ds_names)
+
+ for name, cls in zip(ds_names, ds_list):
+ myrep = events.ReportEventStack(
+ name="search-%s" % name.replace("DataSource", ""),
+ description="searching for %s data from %s" % (mode, name),
+ message="no %s data found from %s" % (mode, name),
+ parent=reporter)
try:
- LOG.debug("Seeing if we can get any data from %s", cls)
- s = cls(sys_cfg, distro, paths)
- if s.get_data():
- return (s, type_utils.obj_name(cls))
+ with myrep:
+ LOG.debug("Seeing if we can get any data from %s", cls)
+ s = cls(sys_cfg, distro, paths)
+ if s.get_data():
+ myrep.message = "found %s data from %s" % (mode, name)
+ return (s, type_utils.obj_name(cls))
except Exception:
util.logexc(LOG, "Getting data from %s failed", cls)
@@ -285,6 +307,18 @@ def list_sources(cfg_list, depends, pkg_list):
return src_list
+def instance_id_matches_system_uuid(instance_id, field='system-uuid'):
+ # quickly (local check only) if self.instance_id is still valid
+ # we check kernel command line or files.
+ if not instance_id:
+ return False
+
+ dmi_value = util.read_dmi_data(field)
+ if not dmi_value:
+ return False
+ return instance_id.lower() == dmi_value.lower()
+
+
# 'depends' is a list of dependencies (DEP_FILESYSTEM)
# ds_list is a list of 2 item lists
# ds_list = [
diff --git a/cloudinit/sources/helpers/azure.py b/cloudinit/sources/helpers/azure.py
new file mode 100644
index 00000000..018cac6d
--- /dev/null
+++ b/cloudinit/sources/helpers/azure.py
@@ -0,0 +1,278 @@
+import logging
+import os
+import re
+import socket
+import struct
+import tempfile
+import time
+from contextlib import contextmanager
+from xml.etree import ElementTree
+
+from cloudinit import util
+
+
+LOG = logging.getLogger(__name__)
+
+
+@contextmanager
+def cd(newdir):
+ prevdir = os.getcwd()
+ os.chdir(os.path.expanduser(newdir))
+ try:
+ yield
+ finally:
+ os.chdir(prevdir)
+
+
+class AzureEndpointHttpClient(object):
+
+ headers = {
+ 'x-ms-agent-name': 'WALinuxAgent',
+ 'x-ms-version': '2012-11-30',
+ }
+
+ def __init__(self, certificate):
+ self.extra_secure_headers = {
+ "x-ms-cipher-name": "DES_EDE3_CBC",
+ "x-ms-guest-agent-public-x509-cert": certificate,
+ }
+
+ def get(self, url, secure=False):
+ headers = self.headers
+ if secure:
+ headers = self.headers.copy()
+ headers.update(self.extra_secure_headers)
+ return util.read_file_or_url(url, headers=headers)
+
+ def post(self, url, data=None, extra_headers=None):
+ headers = self.headers
+ if extra_headers is not None:
+ headers = self.headers.copy()
+ headers.update(extra_headers)
+ return util.read_file_or_url(url, data=data, headers=headers)
+
+
+class GoalState(object):
+
+ def __init__(self, xml, http_client):
+ self.http_client = http_client
+ self.root = ElementTree.fromstring(xml)
+ self._certificates_xml = None
+
+ def _text_from_xpath(self, xpath):
+ element = self.root.find(xpath)
+ if element is not None:
+ return element.text
+ return None
+
+ @property
+ def container_id(self):
+ return self._text_from_xpath('./Container/ContainerId')
+
+ @property
+ def incarnation(self):
+ return self._text_from_xpath('./Incarnation')
+
+ @property
+ def instance_id(self):
+ return self._text_from_xpath(
+ './Container/RoleInstanceList/RoleInstance/InstanceId')
+
+ @property
+ def certificates_xml(self):
+ if self._certificates_xml is None:
+ url = self._text_from_xpath(
+ './Container/RoleInstanceList/RoleInstance'
+ '/Configuration/Certificates')
+ if url is not None:
+ self._certificates_xml = self.http_client.get(
+ url, secure=True).contents
+ return self._certificates_xml
+
+
+class OpenSSLManager(object):
+
+ certificate_names = {
+ 'private_key': 'TransportPrivate.pem',
+ 'certificate': 'TransportCert.pem',
+ }
+
+ def __init__(self):
+ self.tmpdir = tempfile.mkdtemp()
+ self.certificate = None
+ self.generate_certificate()
+
+ def clean_up(self):
+ util.del_dir(self.tmpdir)
+
+ def generate_certificate(self):
+ LOG.debug('Generating certificate for communication with fabric...')
+ if self.certificate is not None:
+ LOG.debug('Certificate already generated.')
+ return
+ with cd(self.tmpdir):
+ util.subp([
+ 'openssl', 'req', '-x509', '-nodes', '-subj',
+ '/CN=LinuxTransport', '-days', '32768', '-newkey', 'rsa:2048',
+ '-keyout', self.certificate_names['private_key'],
+ '-out', self.certificate_names['certificate'],
+ ])
+ certificate = ''
+ for line in open(self.certificate_names['certificate']):
+ if "CERTIFICATE" not in line:
+ certificate += line.rstrip()
+ self.certificate = certificate
+ LOG.debug('New certificate generated.')
+
+ def parse_certificates(self, certificates_xml):
+ tag = ElementTree.fromstring(certificates_xml).find(
+ './/Data')
+ certificates_content = tag.text
+ lines = [
+ b'MIME-Version: 1.0',
+ b'Content-Disposition: attachment; filename="Certificates.p7m"',
+ b'Content-Type: application/x-pkcs7-mime; name="Certificates.p7m"',
+ b'Content-Transfer-Encoding: base64',
+ b'',
+ certificates_content.encode('utf-8'),
+ ]
+ with cd(self.tmpdir):
+ with open('Certificates.p7m', 'wb') as f:
+ f.write(b'\n'.join(lines))
+ out, _ = util.subp(
+ 'openssl cms -decrypt -in Certificates.p7m -inkey'
+ ' {private_key} -recip {certificate} | openssl pkcs12 -nodes'
+ ' -password pass:'.format(**self.certificate_names),
+ shell=True)
+ private_keys, certificates = [], []
+ current = []
+ for line in out.splitlines():
+ current.append(line)
+ if re.match(r'[-]+END .*?KEY[-]+$', line):
+ private_keys.append('\n'.join(current))
+ current = []
+ elif re.match(r'[-]+END .*?CERTIFICATE[-]+$', line):
+ certificates.append('\n'.join(current))
+ current = []
+ keys = []
+ for certificate in certificates:
+ with cd(self.tmpdir):
+ public_key, _ = util.subp(
+ 'openssl x509 -noout -pubkey |'
+ 'ssh-keygen -i -m PKCS8 -f /dev/stdin',
+ data=certificate,
+ shell=True)
+ keys.append(public_key)
+ return keys
+
+
+class WALinuxAgentShim(object):
+
+ REPORT_READY_XML_TEMPLATE = '\n'.join([
+ '<?xml version="1.0" encoding="utf-8"?>',
+ '<Health xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
+ ' xmlns:xsd="http://www.w3.org/2001/XMLSchema">',
+ ' <GoalStateIncarnation>{incarnation}</GoalStateIncarnation>',
+ ' <Container>',
+ ' <ContainerId>{container_id}</ContainerId>',
+ ' <RoleInstanceList>',
+ ' <Role>',
+ ' <InstanceId>{instance_id}</InstanceId>',
+ ' <Health>',
+ ' <State>Ready</State>',
+ ' </Health>',
+ ' </Role>',
+ ' </RoleInstanceList>',
+ ' </Container>',
+ '</Health>'])
+
+ def __init__(self):
+ LOG.debug('WALinuxAgentShim instantiated...')
+ self.endpoint = self.find_endpoint()
+ self.openssl_manager = None
+ self.values = {}
+
+ def clean_up(self):
+ if self.openssl_manager is not None:
+ self.openssl_manager.clean_up()
+
+ @staticmethod
+ def get_ip_from_lease_value(lease_value):
+ unescaped_value = lease_value.replace('\\', '')
+ if len(unescaped_value) > 4:
+ hex_string = ''
+ for hex_pair in unescaped_value.split(':'):
+ if len(hex_pair) == 1:
+ hex_pair = '0' + hex_pair
+ hex_string += hex_pair
+ packed_bytes = struct.pack(
+ '>L', int(hex_string.replace(':', ''), 16))
+ else:
+ packed_bytes = unescaped_value.encode('utf-8')
+ return socket.inet_ntoa(packed_bytes)
+
+ @staticmethod
+ def find_endpoint():
+ LOG.debug('Finding Azure endpoint...')
+ content = util.load_file('/var/lib/dhcp/dhclient.eth0.leases')
+ value = None
+ for line in content.splitlines():
+ if 'unknown-245' in line:
+ value = line.strip(' ').split(' ', 2)[-1].strip(';\n"')
+ if value is None:
+ raise Exception('No endpoint found in DHCP config.')
+ endpoint_ip_address = WALinuxAgentShim.get_ip_from_lease_value(value)
+ LOG.debug('Azure endpoint found at %s', endpoint_ip_address)
+ return endpoint_ip_address
+
+ def register_with_azure_and_fetch_data(self):
+ self.openssl_manager = OpenSSLManager()
+ http_client = AzureEndpointHttpClient(self.openssl_manager.certificate)
+ LOG.info('Registering with Azure...')
+ attempts = 0
+ while True:
+ try:
+ response = http_client.get(
+ 'http://{0}/machine/?comp=goalstate'.format(self.endpoint))
+ except Exception:
+ if attempts < 10:
+ time.sleep(attempts + 1)
+ else:
+ raise
+ else:
+ break
+ attempts += 1
+ LOG.debug('Successfully fetched GoalState XML.')
+ goal_state = GoalState(response.contents, http_client)
+ public_keys = []
+ if goal_state.certificates_xml is not None:
+ LOG.debug('Certificate XML found; parsing out public keys.')
+ public_keys = self.openssl_manager.parse_certificates(
+ goal_state.certificates_xml)
+ data = {
+ 'public-keys': public_keys,
+ }
+ self._report_ready(goal_state, http_client)
+ return data
+
+ def _report_ready(self, goal_state, http_client):
+ LOG.debug('Reporting ready to Azure fabric.')
+ document = self.REPORT_READY_XML_TEMPLATE.format(
+ incarnation=goal_state.incarnation,
+ container_id=goal_state.container_id,
+ instance_id=goal_state.instance_id,
+ )
+ http_client.post(
+ "http://{0}/machine?comp=health".format(self.endpoint),
+ data=document,
+ extra_headers={'Content-Type': 'text/xml; charset=utf-8'},
+ )
+ LOG.info('Reported ready to Azure fabric.')
+
+
+def get_metadata_from_fabric():
+ shim = WALinuxAgentShim()
+ try:
+ return shim.register_with_azure_and_fetch_data()
+ finally:
+ shim.clean_up()
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
index b7e19314..1aa6bbae 100644
--- a/cloudinit/sources/helpers/openstack.py
+++ b/cloudinit/sources/helpers/openstack.py
@@ -24,6 +24,8 @@ import copy
import functools
import os
+import six
+
from cloudinit import ec2_utils
from cloudinit import log as logging
from cloudinit import sources
@@ -49,11 +51,13 @@ OS_LATEST = 'latest'
OS_FOLSOM = '2012-08-10'
OS_GRIZZLY = '2013-04-04'
OS_HAVANA = '2013-10-17'
+OS_LIBERTY = '2015-10-15'
# keep this in chronological order. new supported versions go at the end.
OS_VERSIONS = (
OS_FOLSOM,
OS_GRIZZLY,
OS_HAVANA,
+ OS_LIBERTY,
)
@@ -205,7 +209,7 @@ class BaseReader(object):
"""
load_json_anytype = functools.partial(
- util.load_json, root_types=(dict, basestring, list))
+ util.load_json, root_types=(dict, list) + six.string_types)
def datafiles(version):
files = {}
@@ -227,6 +231,11 @@ class BaseReader(object):
False,
load_json_anytype,
)
+ files['networkdata'] = (
+ self._path_join("openstack", version, 'network_data.json'),
+ False,
+ load_json_anytype,
+ )
return files
results = {
@@ -234,7 +243,7 @@ class BaseReader(object):
'version': 2,
}
data = datafiles(self._find_working_version())
- for (name, (path, required, translator)) in data.iteritems():
+ for (name, (path, required, translator)) in data.items():
path = self._path_join(self.base_path, path)
data = None
found = False
@@ -325,14 +334,14 @@ class ConfigDriveReader(BaseReader):
return os.path.join(*components)
def _path_read(self, path):
- return util.load_file(path)
+ return util.load_file(path, decode=False)
def _fetch_available_versions(self):
if self._versions is None:
path = self._path_join(self.base_path, 'openstack')
found = [d for d in os.listdir(path)
if os.path.isdir(os.path.join(path))]
- self._versions = found
+ self._versions = sorted(found)
return self._versions
def _read_ec2_metadata(self):
@@ -364,7 +373,7 @@ class ConfigDriveReader(BaseReader):
raise NonReadable("%s: no files found" % (self.base_path))
md = {}
- for (name, (key, translator, default)) in FILES_V1.iteritems():
+ for (name, (key, translator, default)) in FILES_V1.items():
if name in found:
path = found[name]
try:
@@ -478,7 +487,7 @@ def convert_vendordata_json(data, recurse=True):
"""
if not data:
return None
- if isinstance(data, (str, unicode, basestring)):
+ if isinstance(data, six.string_types):
return data
if isinstance(data, list):
return copy.deepcopy(data)
diff --git a/cloudinit/sources/helpers/vmware/__init__.py b/cloudinit/sources/helpers/vmware/__init__.py
new file mode 100644
index 00000000..386225d5
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/__init__.py
@@ -0,0 +1,13 @@
+# vi: ts=4 expandtab
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
diff --git a/cloudinit/sources/helpers/vmware/imc/__init__.py b/cloudinit/sources/helpers/vmware/imc/__init__.py
new file mode 100644
index 00000000..386225d5
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/__init__.py
@@ -0,0 +1,13 @@
+# vi: ts=4 expandtab
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
diff --git a/cloudinit/sources/helpers/vmware/imc/boot_proto.py b/cloudinit/sources/helpers/vmware/imc/boot_proto.py
new file mode 100644
index 00000000..faba5887
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/boot_proto.py
@@ -0,0 +1,25 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class BootProtoEnum:
+ """Specifies the NIC Boot Settings."""
+
+ DHCP = 'dhcp'
+ STATIC = 'static'
diff --git a/cloudinit/sources/helpers/vmware/imc/config.py b/cloudinit/sources/helpers/vmware/imc/config.py
new file mode 100644
index 00000000..aebc12a0
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/config.py
@@ -0,0 +1,95 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from .nic import Nic
+
+
+class Config:
+ """
+ Stores the Contents specified in the Customization
+ Specification file.
+ """
+
+ DNS = 'DNS|NAMESERVER|'
+ SUFFIX = 'DNS|SUFFIX|'
+ PASS = 'PASSWORD|-PASS'
+ TIMEZONE = 'DATETIME|TIMEZONE'
+ UTC = 'DATETIME|UTC'
+ HOSTNAME = 'NETWORK|HOSTNAME'
+ DOMAINNAME = 'NETWORK|DOMAINNAME'
+
+ def __init__(self, configFile):
+ self._configFile = configFile
+
+ @property
+ def host_name(self):
+ """Return the hostname."""
+ return self._configFile.get(Config.HOSTNAME, None)
+
+ @property
+ def domain_name(self):
+ """Return the domain name."""
+ return self._configFile.get(Config.DOMAINNAME, None)
+
+ @property
+ def timezone(self):
+ """Return the timezone."""
+ return self._configFile.get(Config.TIMEZONE, None)
+
+ @property
+ def utc(self):
+ """Retrieves whether to set time to UTC or Local."""
+ return self._configFile.get(Config.UTC, None)
+
+ @property
+ def admin_password(self):
+ """Return the root password to be set."""
+ return self._configFile.get(Config.PASS, None)
+
+ @property
+ def name_servers(self):
+ """Return the list of DNS servers."""
+ res = []
+ cnt = self._configFile.get_count_with_prefix(Config.DNS)
+ for i in range(1, cnt + 1):
+ key = Config.DNS + str(i)
+ res.append(self._configFile[key])
+
+ return res
+
+ @property
+ def dns_suffixes(self):
+ """Return the list of DNS Suffixes."""
+ res = []
+ cnt = self._configFile.get_count_with_prefix(Config.SUFFIX)
+ for i in range(1, cnt + 1):
+ key = Config.SUFFIX + str(i)
+ res.append(self._configFile[key])
+
+ return res
+
+ @property
+ def nics(self):
+ """Return the list of associated NICs."""
+ res = []
+ nics = self._configFile['NIC-CONFIG|NICS']
+ for nic in nics.split(','):
+ res.append(Nic(nic, self._configFile))
+
+ return res
diff --git a/cloudinit/sources/helpers/vmware/imc/config_file.py b/cloudinit/sources/helpers/vmware/imc/config_file.py
new file mode 100644
index 00000000..bb9fb7dc
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/config_file.py
@@ -0,0 +1,129 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
+
+from .config_source import ConfigSource
+
+logger = logging.getLogger(__name__)
+
+
+class ConfigFile(ConfigSource, dict):
+ """ConfigFile module to load the content from a specified source."""
+
+ def __init__(self, filename):
+ self._loadConfigFile(filename)
+ pass
+
+ def _insertKey(self, key, val):
+ """
+ Inserts a Key Value pair.
+
+ Keyword arguments:
+ key -- The key to insert
+ val -- The value to insert for the key
+
+ """
+ key = key.strip()
+ val = val.strip()
+
+ if key.startswith('-') or '|-' in key:
+ canLog = False
+ else:
+ canLog = True
+
+ # "sensitive" settings shall not be logged
+ if canLog:
+ logger.debug("ADDED KEY-VAL :: '%s' = '%s'" % (key, val))
+ else:
+ logger.debug("ADDED KEY-VAL :: '%s' = '*****************'" % key)
+
+ self[key] = val
+
+ def _loadConfigFile(self, filename):
+ """
+ Parses properties from the specified config file.
+
+ Any previously available properties will be removed.
+ Sensitive data will not be logged in case the key starts
+ from '-'.
+
+ Keyword arguments:
+ filename - The full path to the config file.
+ """
+ logger.info('Parsing the config file %s.' % filename)
+
+ config = configparser.ConfigParser()
+ config.optionxform = str
+ config.read(filename)
+
+ self.clear()
+
+ for category in config.sections():
+ logger.debug("FOUND CATEGORY = '%s'" % category)
+
+ for (key, value) in config.items(category):
+ self._insertKey(category + '|' + key, value)
+
+ def should_keep_current_value(self, key):
+ """
+ Determines whether a value for a property must be kept.
+
+ If the propery is missing, it is treated as it should be not
+ changed by the engine.
+
+ Keyword arguments:
+ key -- The key to search for.
+ """
+ # helps to distinguish from "empty" value which is used to indicate
+ # "removal"
+ return key not in self
+
+ def should_remove_current_value(self, key):
+ """
+ Determines whether a value for the property must be removed.
+
+ If the specified key is empty, it is treated as it should be
+ removed by the engine.
+
+ Return true if the value can be removed, false otherwise.
+
+ Keyword arguments:
+ key -- The key to search for.
+ """
+ # helps to distinguish from "missing" value which is used to indicate
+ # "keeping unchanged"
+ if key in self:
+ return not bool(self[key])
+ else:
+ return False
+
+ def get_count_with_prefix(self, prefix):
+ """
+ Return the total count of keys that start with the specified prefix.
+
+ Keyword arguments:
+ prefix -- prefix of the key
+ """
+ return len([key for key in self if key.startswith(prefix)])
diff --git a/cloudinit/sources/helpers/vmware/imc/config_namespace.py b/cloudinit/sources/helpers/vmware/imc/config_namespace.py
new file mode 100644
index 00000000..7266b699
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/config_namespace.py
@@ -0,0 +1,25 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from .config_source import ConfigSource
+
+
+class ConfigNamespace(ConfigSource):
+ """Specifies the Config Namespace."""
+ pass
diff --git a/cloudinit/sources/helpers/vmware/imc/config_nic.py b/cloudinit/sources/helpers/vmware/imc/config_nic.py
new file mode 100644
index 00000000..77098a05
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/config_nic.py
@@ -0,0 +1,247 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2016 VMware INC.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+import os
+import re
+
+from cloudinit import util
+
+logger = logging.getLogger(__name__)
+
+
+class NicConfigurator:
+ def __init__(self, nics):
+ """
+ Initialize the Nic Configurator
+ @param nics (list) an array of nics to configure
+ """
+ self.nics = nics
+ self.mac2Name = {}
+ self.ipv4PrimaryGateway = None
+ self.ipv6PrimaryGateway = None
+ self.find_devices()
+ self._primaryNic = self.get_primary_nic()
+
+ def get_primary_nic(self):
+ """
+ Retrieve the primary nic if it exists
+ @return (NicBase): the primary nic if exists, None otherwise
+ """
+ primary_nics = [nic for nic in self.nics if nic.primary]
+ if not primary_nics:
+ return None
+ elif len(primary_nics) > 1:
+ raise Exception('There can only be one primary nic',
+ [nic.mac for nic in primary_nics])
+ else:
+ return primary_nics[0]
+
+ def find_devices(self):
+ """
+ Create the mac2Name dictionary
+ The mac address(es) are in the lower case
+ """
+ cmd = ['ip', 'addr', 'show']
+ (output, err) = util.subp(cmd)
+ sections = re.split(r'\n\d+: ', '\n' + output)[1:]
+
+ macPat = r'link/ether (([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2}))'
+ for section in sections:
+ match = re.search(macPat, section)
+ if not match: # Only keep info about nics
+ continue
+ mac = match.group(1).lower()
+ name = section.split(':', 1)[0]
+ self.mac2Name[mac] = name
+
+ def gen_one_nic(self, nic):
+ """
+ Return the lines needed to configure a nic
+ @return (str list): the string list to configure the nic
+ @param nic (NicBase): the nic to configure
+ """
+ lines = []
+ name = self.mac2Name.get(nic.mac.lower())
+ if not name:
+ raise ValueError('No known device has MACADDR: %s' % nic.mac)
+
+ if nic.onboot:
+ lines.append('auto %s' % name)
+
+ # Customize IPv4
+ lines.extend(self.gen_ipv4(name, nic))
+
+ # Customize IPv6
+ lines.extend(self.gen_ipv6(name, nic))
+
+ lines.append('')
+
+ return lines
+
+ def gen_ipv4(self, name, nic):
+ """
+ Return the lines needed to configure the IPv4 setting of a nic
+ @return (str list): the string list to configure the gateways
+ @param name (str): name of the nic
+ @param nic (NicBase): the nic to configure
+ """
+ lines = []
+
+ bootproto = nic.bootProto.lower()
+ if nic.ipv4_mode.lower() == 'disabled':
+ bootproto = 'manual'
+ lines.append('iface %s inet %s' % (name, bootproto))
+
+ if bootproto != 'static':
+ return lines
+
+ # Static Ipv4
+ v4 = nic.staticIpv4
+ if v4.ip:
+ lines.append(' address %s' % v4.ip)
+ if v4.netmask:
+ lines.append(' netmask %s' % v4.netmask)
+
+ # Add the primary gateway
+ if nic.primary and v4.gateways:
+ self.ipv4PrimaryGateway = v4.gateways[0]
+ lines.append(' gateway %s metric 0' % self.ipv4PrimaryGateway)
+ return lines
+
+ # Add routes if there is no primary nic
+ if not self._primaryNic:
+ lines.extend(self.gen_ipv4_route(nic, v4.gateways))
+
+ return lines
+
+ def gen_ipv4_route(self, nic, gateways):
+ """
+ Return the lines needed to configure additional Ipv4 route
+ @return (str list): the string list to configure the gateways
+ @param nic (NicBase): the nic to configure
+ @param gateways (str list): the list of gateways
+ """
+ lines = []
+
+ for gateway in gateways:
+ lines.append(' up route add default gw %s metric 10000' %
+ gateway)
+
+ return lines
+
+ def gen_ipv6(self, name, nic):
+ """
+ Return the lines needed to configure the gateways for a nic
+ @return (str list): the string list to configure the gateways
+ @param name (str): name of the nic
+ @param nic (NicBase): the nic to configure
+ """
+ lines = []
+
+ if not nic.staticIpv6:
+ return lines
+
+ # Static Ipv6
+ addrs = nic.staticIpv6
+ lines.append('iface %s inet6 static' % name)
+ lines.append(' address %s' % addrs[0].ip)
+ lines.append(' netmask %s' % addrs[0].netmask)
+
+ for addr in addrs[1:]:
+ lines.append(' up ifconfig %s inet6 add %s/%s' % (name, addr.ip,
+ addr.netmask))
+ # Add the primary gateway
+ if nic.primary:
+ for addr in addrs:
+ if addr.gateway:
+ self.ipv6PrimaryGateway = addr.gateway
+ lines.append(' gateway %s' % self.ipv6PrimaryGateway)
+ return lines
+
+ # Add routes if there is no primary nic
+ if not self._primaryNic:
+ lines.extend(self._genIpv6Route(name, nic, addrs))
+
+ return lines
+
+ def _genIpv6Route(self, name, nic, addrs):
+ lines = []
+
+ for addr in addrs:
+ lines.append(' up route -A inet6 add default gw '
+ '%s metric 10000' % addr.gateway)
+
+ return lines
+
+ def generate(self):
+ """Return the lines that is needed to configure the nics"""
+ lines = []
+ lines.append('iface lo inet loopback')
+ lines.append('auto lo')
+ lines.append('')
+
+ for nic in self.nics:
+ lines.extend(self.gen_one_nic(nic))
+
+ return lines
+
+ def clear_dhcp(self):
+ logger.info('Clearing DHCP leases')
+
+ # Ignore the return code 1.
+ util.subp(["pkill", "dhclient"], rcs=[0, 1])
+ util.subp(["rm", "-f", "/var/lib/dhcp/*"])
+
+ def if_down_up(self):
+ names = []
+ for nic in self.nics:
+ name = self.mac2Name.get(nic.mac.lower())
+ names.append(name)
+
+ for name in names:
+ logger.info('Bring down interface %s' % name)
+ util.subp(["ifdown", "%s" % name])
+
+ self.clear_dhcp()
+
+ for name in names:
+ logger.info('Bring up interface %s' % name)
+ util.subp(["ifup", "%s" % name])
+
+ def configure(self):
+ """
+ Configure the /etc/network/intefaces
+ Make a back up of the original
+ """
+ containingDir = '/etc/network'
+
+ interfaceFile = os.path.join(containingDir, 'interfaces')
+ originalFile = os.path.join(containingDir,
+ 'interfaces.before_vmware_customization')
+
+ if not os.path.exists(originalFile) and os.path.exists(interfaceFile):
+ os.rename(interfaceFile, originalFile)
+
+ lines = self.generate()
+ with open(interfaceFile, 'w') as fp:
+ for line in lines:
+ fp.write('%s\n' % line)
+
+ self.if_down_up()
diff --git a/cloudinit/sources/helpers/vmware/imc/config_source.py b/cloudinit/sources/helpers/vmware/imc/config_source.py
new file mode 100644
index 00000000..a367e476
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/config_source.py
@@ -0,0 +1,23 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class ConfigSource:
+ """Specifies a source for the Config Content."""
+ pass
diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_error.py b/cloudinit/sources/helpers/vmware/imc/guestcust_error.py
new file mode 100644
index 00000000..1b04161f
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_error.py
@@ -0,0 +1,24 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2016 Canonical Ltd.
+# Copyright (C) 2016 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class GuestCustErrorEnum:
+ """Specifies different errors of Guest Customization engine"""
+
+ GUESTCUST_ERROR_SUCCESS = 0
diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_event.py b/cloudinit/sources/helpers/vmware/imc/guestcust_event.py
new file mode 100644
index 00000000..fc22568f
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_event.py
@@ -0,0 +1,27 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2016 Canonical Ltd.
+# Copyright (C) 2016 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class GuestCustEventEnum:
+ """Specifies different types of Guest Customization Events"""
+
+ GUESTCUST_EVENT_CUSTOMIZE_FAILED = 100
+ GUESTCUST_EVENT_NETWORK_SETUP_FAILED = 101
+ GUESTCUST_EVENT_ENABLE_NICS = 103
+ GUESTCUST_EVENT_QUERY_NICS = 104
diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_state.py b/cloudinit/sources/helpers/vmware/imc/guestcust_state.py
new file mode 100644
index 00000000..f255be5f
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_state.py
@@ -0,0 +1,25 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2016 Canonical Ltd.
+# Copyright (C) 2016 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class GuestCustStateEnum:
+ """Specifies different states of Guest Customization engine"""
+
+ GUESTCUST_STATE_RUNNING = 4
+ GUESTCUST_STATE_DONE = 5
diff --git a/cloudinit/sources/helpers/vmware/imc/guestcust_util.py b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
new file mode 100644
index 00000000..d39f0a65
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/guestcust_util.py
@@ -0,0 +1,128 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2016 Canonical Ltd.
+# Copyright (C) 2016 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+import os
+import time
+
+from cloudinit import util
+
+from .guestcust_state import GuestCustStateEnum
+from .guestcust_event import GuestCustEventEnum
+
+logger = logging.getLogger(__name__)
+
+
+CLOUDINIT_LOG_FILE = "/var/log/cloud-init.log"
+QUERY_NICS_SUPPORTED = "queryNicsSupported"
+NICS_STATUS_CONNECTED = "connected"
+
+
+# This will send a RPC command to the underlying
+# VMware Virtualization Platform.
+def send_rpc(rpc):
+ if not rpc:
+ return None
+
+ out = ""
+ err = "Error sending the RPC command"
+
+ try:
+ logger.debug("Sending RPC command: %s", rpc)
+ (out, err) = util.subp(["vmware-rpctool", rpc], rcs=[0])
+ # Remove the trailing newline in the output.
+ if out:
+ out = out.rstrip()
+ except Exception as e:
+ logger.debug("Failed to send RPC command")
+ logger.exception(e)
+
+ return (out, err)
+
+
+# This will send the customization status to the
+# underlying VMware Virtualization Platform.
+def set_customization_status(custstate, custerror, errormessage=None):
+ message = ""
+
+ if errormessage:
+ message = CLOUDINIT_LOG_FILE + "@" + errormessage
+ else:
+ message = CLOUDINIT_LOG_FILE
+
+ rpc = "deployPkg.update.state %d %d %s" % (custstate, custerror, message)
+ (out, err) = send_rpc(rpc)
+ return (out, err)
+
+
+# This will read the file nics.txt in the specified directory
+# and return the content
+def get_nics_to_enable(dirpath):
+ if not dirpath:
+ return None
+
+ NICS_SIZE = 1024
+ nicsfilepath = os.path.join(dirpath, "nics.txt")
+ if not os.path.exists(nicsfilepath):
+ return None
+
+ with open(nicsfilepath, 'r') as fp:
+ nics = fp.read(NICS_SIZE)
+
+ return nics
+
+
+# This will send a RPC command to the underlying VMware Virtualization platform
+# and enable nics.
+def enable_nics(nics):
+ if not nics:
+ logger.warning("No Nics found")
+ return
+
+ enableNicsWaitRetries = 5
+ enableNicsWaitCount = 5
+ enableNicsWaitSeconds = 1
+
+ for attempt in range(0, enableNicsWaitRetries):
+ logger.debug("Trying to connect interfaces, attempt %d", attempt)
+ (out, err) = set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
+ GuestCustEventEnum.GUESTCUST_EVENT_ENABLE_NICS,
+ nics)
+ if not out:
+ time.sleep(enableNicsWaitCount * enableNicsWaitSeconds)
+ continue
+
+ if out != QUERY_NICS_SUPPORTED:
+ logger.warning("NICS connection status query is not supported")
+ return
+
+ for count in range(0, enableNicsWaitCount):
+ (out, err) = set_customization_status(
+ GuestCustStateEnum.GUESTCUST_STATE_RUNNING,
+ GuestCustEventEnum.GUESTCUST_EVENT_QUERY_NICS,
+ nics)
+ if out and out == NICS_STATUS_CONNECTED:
+ logger.info("NICS are connected on %d second", count)
+ return
+
+ time.sleep(enableNicsWaitSeconds)
+
+ logger.warning("Can't connect network interfaces after %d attempts",
+ enableNicsWaitRetries)
diff --git a/cloudinit/sources/helpers/vmware/imc/ipv4_mode.py b/cloudinit/sources/helpers/vmware/imc/ipv4_mode.py
new file mode 100644
index 00000000..33f88726
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/ipv4_mode.py
@@ -0,0 +1,45 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class Ipv4ModeEnum:
+ """
+ The IPv4 configuration mode which directly represents the user's goal.
+
+ This mode effectively acts as a contract of the in-guest customization
+ engine. It must be set based on what the user has requested and should
+ not be changed by those layers. It's up to the in-guest engine to
+ interpret and materialize the user's request.
+ """
+
+ # The legacy mode which only allows dhcp/static based on whether IPv4
+ # addresses list is empty or not
+ IPV4_MODE_BACKWARDS_COMPATIBLE = 'BACKWARDS_COMPATIBLE'
+
+ # IPv4 must use static address. Reserved for future use
+ IPV4_MODE_STATIC = 'STATIC'
+
+ # IPv4 must use DHCPv4. Reserved for future use
+ IPV4_MODE_DHCP = 'DHCP'
+
+ # IPv4 must be disabled
+ IPV4_MODE_DISABLED = 'DISABLED'
+
+ # IPv4 settings should be left untouched. Reserved for future use
+ IPV4_MODE_AS_IS = 'AS_IS'
diff --git a/cloudinit/sources/helpers/vmware/imc/nic.py b/cloudinit/sources/helpers/vmware/imc/nic.py
new file mode 100644
index 00000000..b5d704ea
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/nic.py
@@ -0,0 +1,147 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from .boot_proto import BootProtoEnum
+from .nic_base import NicBase, StaticIpv4Base, StaticIpv6Base
+
+
+class Nic(NicBase):
+ """
+ Holds the information about each NIC specified
+ in the customization specification file
+ """
+
+ def __init__(self, name, configFile):
+ self._name = name
+ self._configFile = configFile
+
+ def _get(self, what):
+ return self._configFile.get(self.name + '|' + what, None)
+
+ def _get_count_with_prefix(self, prefix):
+ return self._configFile.get_count_with_prefix(self.name + prefix)
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def mac(self):
+ return self._get('MACADDR').lower()
+
+ @property
+ def primary(self):
+ value = self._get('PRIMARY')
+ if value:
+ value = value.lower()
+ return value == 'yes' or value == 'true'
+ else:
+ return False
+
+ @property
+ def onboot(self):
+ value = self._get('ONBOOT')
+ if value:
+ value = value.lower()
+ return value == 'yes' or value == 'true'
+ else:
+ return False
+
+ @property
+ def bootProto(self):
+ value = self._get('BOOTPROTO')
+ if value:
+ return value.lower()
+ else:
+ return ""
+
+ @property
+ def ipv4_mode(self):
+ value = self._get('IPv4_MODE')
+ if value:
+ return value.lower()
+ else:
+ return ""
+
+ @property
+ def staticIpv4(self):
+ """
+ Checks the BOOTPROTO property and returns StaticIPv4Addr
+ configuration object if STATIC configuration is set.
+ """
+ if self.bootProto == BootProtoEnum.STATIC:
+ return [StaticIpv4Addr(self)]
+ else:
+ return None
+
+ @property
+ def staticIpv6(self):
+ cnt = self._get_count_with_prefix('|IPv6ADDR|')
+
+ if not cnt:
+ return None
+
+ result = []
+ for index in range(1, cnt + 1):
+ result.append(StaticIpv6Addr(self, index))
+
+ return result
+
+
+class StaticIpv4Addr(StaticIpv4Base):
+ """Static IPV4 Setting."""
+
+ def __init__(self, nic):
+ self._nic = nic
+
+ @property
+ def ip(self):
+ return self._nic._get('IPADDR')
+
+ @property
+ def netmask(self):
+ return self._nic._get('NETMASK')
+
+ @property
+ def gateways(self):
+ value = self._nic._get('GATEWAY')
+ if value:
+ return [x.strip() for x in value.split(',')]
+ else:
+ return None
+
+
+class StaticIpv6Addr(StaticIpv6Base):
+ """Static IPV6 Address."""
+
+ def __init__(self, nic, index):
+ self._nic = nic
+ self._index = index
+
+ @property
+ def ip(self):
+ return self._nic._get('IPv6ADDR|' + str(self._index))
+
+ @property
+ def netmask(self):
+ return self._nic._get('IPv6NETMASK|' + str(self._index))
+
+ @property
+ def gateway(self):
+ return self._nic._get('IPv6GATEWAY|' + str(self._index))
diff --git a/cloudinit/sources/helpers/vmware/imc/nic_base.py b/cloudinit/sources/helpers/vmware/imc/nic_base.py
new file mode 100644
index 00000000..030ba311
--- /dev/null
+++ b/cloudinit/sources/helpers/vmware/imc/nic_base.py
@@ -0,0 +1,154 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2015 VMware Inc.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+class NicBase:
+ """
+ Define what are expected of each nic.
+ The following properties should be provided in an implementation class.
+ """
+
+ @property
+ def mac(self):
+ """
+ Retrieves the mac address of the nic
+ @return (str) : the MACADDR setting
+ """
+ raise NotImplementedError('MACADDR')
+
+ @property
+ def primary(self):
+ """
+ Retrieves whether the nic is the primary nic
+ Indicates whether NIC will be used to define the default gateway.
+ If none of the NICs is configured to be primary, default gateway won't
+ be set.
+ @return (bool): the PRIMARY setting
+ """
+ raise NotImplementedError('PRIMARY')
+
+ @property
+ def onboot(self):
+ """
+ Retrieves whether the nic should be up at the boot time
+ @return (bool) : the ONBOOT setting
+ """
+ raise NotImplementedError('ONBOOT')
+
+ @property
+ def bootProto(self):
+ """
+ Retrieves the boot protocol of the nic
+ @return (str): the BOOTPROTO setting, valid values: dhcp and static.
+ """
+ raise NotImplementedError('BOOTPROTO')
+
+ @property
+ def ipv4_mode(self):
+ """
+ Retrieves the IPv4_MODE
+ @return (str): the IPv4_MODE setting, valid values:
+ backwards_compatible, static, dhcp, disabled, as_is
+ """
+ raise NotImplementedError('IPv4_MODE')
+
+ @property
+ def staticIpv4(self):
+ """
+ Retrieves the static IPv4 configuration of the nic
+ @return (StaticIpv4Base list): the static ipv4 setting
+ """
+ raise NotImplementedError('Static IPv4')
+
+ @property
+ def staticIpv6(self):
+ """
+ Retrieves the IPv6 configuration of the nic
+ @return (StaticIpv6Base list): the static ipv6 setting
+ """
+ raise NotImplementedError('Static Ipv6')
+
+ def validate(self):
+ """
+ Validate the object
+ For example, the staticIpv4 property is required and should not be
+ empty when ipv4Mode is STATIC
+ """
+ raise NotImplementedError('Check constraints on properties')
+
+
+class StaticIpv4Base:
+ """
+ Define what are expected of a static IPv4 setting
+ The following properties should be provided in an implementation class.
+ """
+
+ @property
+ def ip(self):
+ """
+ Retrieves the Ipv4 address
+ @return (str): the IPADDR setting
+ """
+ raise NotImplementedError('Ipv4 Address')
+
+ @property
+ def netmask(self):
+ """
+ Retrieves the Ipv4 NETMASK setting
+ @return (str): the NETMASK setting
+ """
+ raise NotImplementedError('Ipv4 NETMASK')
+
+ @property
+ def gateways(self):
+ """
+ Retrieves the gateways on this Ipv4 subnet
+ @return (str list): the GATEWAY setting
+ """
+ raise NotImplementedError('Ipv4 GATEWAY')
+
+
+class StaticIpv6Base:
+ """Define what are expected of a static IPv6 setting
+ The following properties should be provided in an implementation class.
+ """
+
+ @property
+ def ip(self):
+ """
+ Retrieves the Ipv6 address
+ @return (str): the IPv6ADDR setting
+ """
+ raise NotImplementedError('Ipv6 Address')
+
+ @property
+ def netmask(self):
+ """
+ Retrieves the Ipv6 NETMASK setting
+ @return (str): the IPv6NETMASK setting
+ """
+ raise NotImplementedError('Ipv6 NETMASK')
+
+ @property
+ def gateway(self):
+ """
+ Retrieves the Ipv6 GATEWAY setting
+ @return (str): the IPv6GATEWAY setting
+ """
+ raise NotImplementedError('Ipv6 GATEWAY')
diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py
index 14d0cb0f..c74a7ae2 100644
--- a/cloudinit/ssh_util.py
+++ b/cloudinit/ssh_util.py
@@ -31,7 +31,8 @@ LOG = logging.getLogger(__name__)
DEF_SSHD_CFG = "/etc/ssh/sshd_config"
# taken from openssh source key.c/key_type_from_name
-VALID_KEY_TYPES = ("rsa", "dsa", "ssh-rsa", "ssh-dss", "ecdsa",
+VALID_KEY_TYPES = (
+ "rsa", "dsa", "ssh-rsa", "ssh-dss", "ecdsa",
"ssh-rsa-cert-v00@openssh.com", "ssh-dss-cert-v00@openssh.com",
"ssh-rsa-cert-v00@openssh.com", "ssh-dss-cert-v00@openssh.com",
"ssh-rsa-cert-v01@openssh.com", "ssh-dss-cert-v01@openssh.com",
@@ -239,7 +240,7 @@ def setup_user_keys(keys, username, options=None):
# Make sure the users .ssh dir is setup accordingly
(ssh_dir, pwent) = users_ssh_info(username)
if not os.path.isdir(ssh_dir):
- util.ensure_dir(ssh_dir, mode=0700)
+ util.ensure_dir(ssh_dir, mode=0o700)
util.chownbyid(ssh_dir, pwent.pw_uid, pwent.pw_gid)
# Turn the 'update' keys given into actual entries
@@ -252,8 +253,8 @@ def setup_user_keys(keys, username, options=None):
(auth_key_fn, auth_key_entries) = extract_authorized_keys(username)
with util.SeLinuxGuard(ssh_dir, recursive=True):
content = update_authorized_keys(auth_key_entries, key_entries)
- util.ensure_dir(os.path.dirname(auth_key_fn), mode=0700)
- util.write_file(auth_key_fn, content, mode=0600)
+ util.ensure_dir(os.path.dirname(auth_key_fn), mode=0o700)
+ util.write_file(auth_key_fn, content, mode=0o600)
util.chownbyid(auth_key_fn, pwent.pw_uid, pwent.pw_gid)
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 67f467f7..3fbb4443 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -20,12 +20,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import cPickle as pickle
-
import copy
import os
import sys
+import six
+from six.moves import cPickle as pickle
+
from cloudinit.settings import (PER_INSTANCE, FREQUENCIES, CLOUD_CONFIG)
from cloudinit import handlers
@@ -42,9 +43,11 @@ from cloudinit import distros
from cloudinit import helpers
from cloudinit import importer
from cloudinit import log as logging
+from cloudinit import net
from cloudinit import sources
from cloudinit import type_utils
from cloudinit import util
+from cloudinit.reporting import events
LOG = logging.getLogger(__name__)
@@ -52,7 +55,7 @@ NULL_DATA_SOURCE = None
class Init(object):
- def __init__(self, ds_deps=None):
+ def __init__(self, ds_deps=None, reporter=None):
if ds_deps is not None:
self.ds_deps = ds_deps
else:
@@ -64,6 +67,12 @@ class Init(object):
# Changed only when a fetch occurs
self.datasource = NULL_DATA_SOURCE
+ if reporter is None:
+ reporter = events.ReportEventStack(
+ name="init-reporter", description="init-desc",
+ reporting_enabled=False)
+ self.reporter = reporter
+
def _reset(self, reset_ds=False):
# Recreated on access
self._cfg = None
@@ -132,7 +141,7 @@ class Init(object):
]
return initial_dirs
- def purge_cache(self, rm_instance_lnk=True):
+ def purge_cache(self, rm_instance_lnk=False):
rm_list = []
rm_list.append(self.paths.boot_finished)
if rm_instance_lnk:
@@ -147,16 +156,25 @@ class Init(object):
def _initialize_filesystem(self):
util.ensure_dirs(self._initial_subdirs())
log_file = util.get_cfg_option_str(self.cfg, 'def_log_file')
- perms = util.get_cfg_option_str(self.cfg, 'syslog_fix_perms')
if log_file:
util.ensure_file(log_file)
- if perms:
- u, g = util.extract_usergroup(perms)
+ perms = self.cfg.get('syslog_fix_perms')
+ if not perms:
+ perms = {}
+ if not isinstance(perms, list):
+ perms = [perms]
+
+ error = None
+ for perm in perms:
+ u, g = util.extract_usergroup(perm)
try:
util.chownbyname(log_file, u, g)
- except OSError:
- util.logexc(LOG, "Unable to change the ownership of %s to "
- "user %s, group %s", log_file, u, g)
+ return
+ except OSError as e:
+ error = e
+
+ LOG.warn("Failed changing perms on '%s'. tried: %s. %s",
+ log_file, ','.join(perms), error)
def read_cfg(self, extra_fns=None):
# None check so that we don't keep on re-loading if empty
@@ -176,37 +194,12 @@ class Init(object):
# We try to restore from a current link and static path
# by using the instance link, if purge_cache was called
# the file wont exist.
- pickled_fn = self.paths.get_ipath_cur('obj_pkl')
- pickle_contents = None
- try:
- pickle_contents = util.load_file(pickled_fn)
- except Exception:
- pass
- # This is expected so just return nothing
- # successfully loaded...
- if not pickle_contents:
- return None
- try:
- return pickle.loads(pickle_contents)
- except Exception:
- util.logexc(LOG, "Failed loading pickled blob from %s", pickled_fn)
- return None
+ return _pkl_load(self.paths.get_ipath_cur('obj_pkl'))
def _write_to_cache(self):
if self.datasource is NULL_DATA_SOURCE:
return False
- pickled_fn = self.paths.get_ipath_cur("obj_pkl")
- try:
- pk_contents = pickle.dumps(self.datasource)
- except Exception:
- util.logexc(LOG, "Failed pickling datasource %s", self.datasource)
- return False
- try:
- util.write_file(pickled_fn, pk_contents, mode=0400)
- except Exception:
- util.logexc(LOG, "Failed pickling datasource to %s", pickled_fn)
- return False
- return True
+ return _pkl_store(self.datasource, self.paths.get_ipath_cur("obj_pkl"))
def _get_datasources(self):
# Any config provided???
@@ -218,13 +211,30 @@ class Init(object):
cfg_list = self.cfg.get('datasource_list') or []
return (cfg_list, pkg_list)
- def _get_data_source(self):
+ def _get_data_source(self, existing):
if self.datasource is not NULL_DATA_SOURCE:
return self.datasource
- ds = self._restore_from_cache()
- if ds:
- LOG.debug("Restored from cache, datasource: %s", ds)
+
+ with events.ReportEventStack(
+ name="check-cache",
+ description="attempting to read from cache [%s]" % existing,
+ parent=self.reporter) as myrep:
+ ds = self._restore_from_cache()
+ if ds and existing == "trust":
+ myrep.description = "restored from cache: %s" % ds
+ elif ds and existing == "check":
+ if (hasattr(ds, 'check_instance_id') and
+ ds.check_instance_id(self.cfg)):
+ myrep.description = "restored from checked cache: %s" % ds
+ else:
+ myrep.description = "cache invalid in datasource: %s" % ds
+ ds = None
+ else:
+ myrep.description = "no cache found"
+ LOG.debug(myrep.description)
+
if not ds:
+ util.del_file(self.paths.instance_link)
(cfg_list, pkg_list) = self._get_datasources()
# Deep copy so that user-data handlers can not modify
# (which will affect user-data handlers down the line...)
@@ -233,7 +243,7 @@ class Init(object):
self.paths,
copy.deepcopy(self.ds_deps),
cfg_list,
- pkg_list)
+ pkg_list, self.reporter)
LOG.info("Loaded datasource %s - %s", dsname, ds)
self.datasource = ds
# Ensure we adjust our path members datasource
@@ -304,8 +314,8 @@ class Init(object):
self._reset()
return iid
- def fetch(self):
- return self._get_data_source()
+ def fetch(self, existing="check"):
+ return self._get_data_source(existing=existing)
def instancify(self):
return self._reflect_cur_instance()
@@ -314,7 +324,8 @@ class Init(object):
# Form the needed options to cloudify our members
return cloud.Cloud(self.datasource,
self.paths, self.cfg,
- self.distro, helpers.Runners(self.paths))
+ self.distro, helpers.Runners(self.paths),
+ reporter=self.reporter)
def update(self):
if not self._write_to_cache():
@@ -323,16 +334,27 @@ class Init(object):
self._store_vendordata()
def _store_userdata(self):
- raw_ud = "%s" % (self.datasource.get_userdata_raw())
- util.write_file(self._get_ipath('userdata_raw'), raw_ud, 0600)
- processed_ud = "%s" % (self.datasource.get_userdata())
- util.write_file(self._get_ipath('userdata'), processed_ud, 0600)
+ raw_ud = self.datasource.get_userdata_raw()
+ if raw_ud is None:
+ raw_ud = b''
+ util.write_file(self._get_ipath('userdata_raw'), raw_ud, 0o600)
+ # processed userdata is a Mime message, so write it as string.
+ processed_ud = self.datasource.get_userdata()
+ if processed_ud is None:
+ raw_ud = ''
+ util.write_file(self._get_ipath('userdata'), str(processed_ud), 0o600)
def _store_vendordata(self):
- raw_vd = "%s" % (self.datasource.get_vendordata_raw())
- util.write_file(self._get_ipath('vendordata_raw'), raw_vd, 0600)
- processed_vd = "%s" % (self.datasource.get_vendordata())
- util.write_file(self._get_ipath('vendordata'), processed_vd, 0600)
+ raw_vd = self.datasource.get_vendordata_raw()
+ if raw_vd is None:
+ raw_vd = b''
+ util.write_file(self._get_ipath('vendordata_raw'), raw_vd, 0o600)
+ # processed vendor data is a Mime message, so write it as string.
+ processed_vd = str(self.datasource.get_vendordata())
+ if processed_vd is None:
+ processed_vd = ''
+ util.write_file(self._get_ipath('vendordata'), str(processed_vd),
+ 0o600)
def _default_handlers(self, opts=None):
if opts is None:
@@ -384,7 +406,7 @@ class Init(object):
if not path or not os.path.isdir(path):
return
potential_handlers = util.find_modules(path)
- for (fname, mod_name) in potential_handlers.iteritems():
+ for (fname, mod_name) in potential_handlers.items():
try:
mod_locs, looked_locs = importer.find_module(
mod_name, [''], ['list_types', 'handle_part'])
@@ -422,7 +444,7 @@ class Init(object):
def init_handlers():
# Init the handlers first
- for (_ctype, mod) in c_handlers.iteritems():
+ for (_ctype, mod) in c_handlers.items():
if mod in c_handlers.initialized:
# Avoid initing the same module twice (if said module
# is registered to more than one content-type).
@@ -449,7 +471,7 @@ class Init(object):
def finalize_handlers():
# Give callbacks opportunity to finalize
- for (_ctype, mod) in c_handlers.iteritems():
+ for (_ctype, mod) in c_handlers.items():
if mod not in c_handlers.initialized:
# Said module was never inited in the first place, so lets
# not attempt to finalize those that never got called.
@@ -469,8 +491,14 @@ class Init(object):
def consume_data(self, frequency=PER_INSTANCE):
# Consume the userdata first, because we need want to let the part
# handlers run first (for merging stuff)
- self._consume_userdata(frequency)
- self._consume_vendordata(frequency)
+ with events.ReportEventStack("consume-user-data",
+ "reading and applying user-data",
+ parent=self.reporter):
+ self._consume_userdata(frequency)
+ with events.ReportEventStack("consume-vendor-data",
+ "reading and applying vendor-data",
+ parent=self.reporter):
+ self._consume_vendordata(frequency)
# Perform post-consumption adjustments so that
# modules that run during the init stage reflect
@@ -541,13 +569,53 @@ class Init(object):
# Run the handlers
self._do_handlers(user_data_msg, c_handlers_list, frequency)
+ def _find_networking_config(self):
+ disable_file = os.path.join(
+ self.paths.get_cpath('data'), 'upgraded-network')
+ if os.path.exists(disable_file):
+ return (None, disable_file)
+
+ cmdline_cfg = ('cmdline', net.read_kernel_cmdline_config())
+ dscfg = ('ds', None)
+ if self.datasource and hasattr(self.datasource, 'network_config'):
+ dscfg = ('ds', self.datasource.network_config)
+ sys_cfg = ('system_cfg', self.cfg.get('network'))
+
+ for loc, ncfg in (cmdline_cfg, dscfg, sys_cfg):
+ if net.is_disabled_cfg(ncfg):
+ LOG.debug("network config disabled by %s", loc)
+ return (None, loc)
+ if ncfg:
+ return (ncfg, loc)
+ return (net.generate_fallback_config(), "fallback")
+
+ def apply_network_config(self):
+ netcfg, src = self._find_networking_config()
+ if netcfg is None:
+ LOG.info("network config is disabled by %s", src)
+ return
+
+ LOG.info("Applying network configuration from %s: %s", src, netcfg)
+ try:
+ return self.distro.apply_network_config(netcfg)
+ except NotImplementedError:
+ LOG.warn("distro '%s' does not implement apply_network_config. "
+ "networking may not be configured properly." %
+ self.distro)
+ return
+
class Modules(object):
- def __init__(self, init, cfg_files=None):
+ def __init__(self, init, cfg_files=None, reporter=None):
self.init = init
self.cfg_files = cfg_files
# Created on first use
self._cached_cfg = None
+ if reporter is None:
+ reporter = events.ReportEventStack(
+ name="module-reporter", description="module-desc",
+ reporting_enabled=False)
+ self.reporter = reporter
@property
def cfg(self):
@@ -574,7 +642,7 @@ class Modules(object):
for item in cfg_mods:
if not item:
continue
- if isinstance(item, (str, basestring)):
+ if isinstance(item, six.string_types):
module_list.append({
'mod': item.strip(),
})
@@ -604,7 +672,7 @@ class Modules(object):
else:
raise TypeError(("Failed to read '%s' item in config,"
" unknown type %s") %
- (item, type_utils.obj_name(item)))
+ (item, type_utils.obj_name(item)))
return module_list
def _fixup_modules(self, raw_mods):
@@ -657,7 +725,19 @@ class Modules(object):
which_ran.append(name)
# This name will affect the semaphore name created
run_name = "config-%s" % (name)
- cc.run(run_name, mod.handle, func_args, freq=freq)
+
+ desc = "running %s with frequency %s" % (run_name, freq)
+ myrep = events.ReportEventStack(
+ name=run_name, description=desc, parent=self.reporter)
+
+ with myrep:
+ ran, _r = cc.run(run_name, mod.handle, func_args,
+ freq=freq)
+ if ran:
+ myrep.message = "%s ran successfully" % run_name
+ else:
+ myrep.message = "%s previously ran" % run_name
+
except Exception as e:
util.logexc(LOG, "Running module %s (%s) failed", name, mod)
failures.append((name, e))
@@ -699,8 +779,8 @@ class Modules(object):
if skipped:
LOG.info("Skipping modules %s because they are not verified "
- "on distro '%s'. To run anyway, add them to "
- "'unverified_modules' in config.", skipped, d_name)
+ "on distro '%s'. To run anyway, add them to "
+ "'unverified_modules' in config.", skipped, d_name)
if forced:
LOG.info("running unverified_modules: %s", forced)
@@ -725,3 +805,36 @@ def fetch_base_config():
base_cfgs.append(default_cfg)
return util.mergemanydict(base_cfgs)
+
+
+def _pkl_store(obj, fname):
+ try:
+ pk_contents = pickle.dumps(obj)
+ except Exception:
+ util.logexc(LOG, "Failed pickling datasource %s", obj)
+ return False
+ try:
+ util.write_file(fname, pk_contents, omode="wb", mode=0o400)
+ except Exception:
+ util.logexc(LOG, "Failed pickling datasource to %s", fname)
+ return False
+ return True
+
+
+def _pkl_load(fname):
+ pickle_contents = None
+ try:
+ pickle_contents = util.load_file(fname, decode=False)
+ except Exception as e:
+ if os.path.isfile(fname):
+ LOG.warn("failed loading pickle in %s: %s" % (fname, e))
+ pass
+
+ # This is allowed so just return nothing successfully loaded...
+ if not pickle_contents:
+ return None
+ try:
+ return pickle.loads(pickle_contents)
+ except Exception:
+ util.logexc(LOG, "Failed loading pickled blob from %s", fname)
+ return None
diff --git a/cloudinit/templater.py b/cloudinit/templater.py
index 4cd3f13d..a9231482 100644
--- a/cloudinit/templater.py
+++ b/cloudinit/templater.py
@@ -137,7 +137,7 @@ def render_from_file(fn, params):
return renderer(content, params)
-def render_to_file(fn, outfn, params, mode=0644):
+def render_to_file(fn, outfn, params, mode=0o644):
contents = render_from_file(fn, params)
util.write_file(outfn, contents, mode=mode)
diff --git a/cloudinit/type_utils.py b/cloudinit/type_utils.py
index cc3d9495..b93efd6a 100644
--- a/cloudinit/type_utils.py
+++ b/cloudinit/type_utils.py
@@ -22,11 +22,31 @@
import types
+import six
+
+
+if six.PY3:
+ _NAME_TYPES = (
+ types.ModuleType,
+ types.FunctionType,
+ types.LambdaType,
+ type,
+ )
+else:
+ _NAME_TYPES = (
+ types.TypeType,
+ types.ModuleType,
+ types.FunctionType,
+ types.LambdaType,
+ types.ClassType,
+ )
+
def obj_name(obj):
- if isinstance(obj, (types.TypeType,
- types.ModuleType,
- types.FunctionType,
- types.LambdaType)):
- return str(obj.__name__)
- return obj_name(obj.__class__)
+ if isinstance(obj, _NAME_TYPES):
+ return six.text_type(obj.__name__)
+ else:
+ if not hasattr(obj, '__class__'):
+ return repr(obj)
+ else:
+ return obj_name(obj.__class__)
diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py
index 3074dd08..936f7da5 100644
--- a/cloudinit/url_helper.py
+++ b/cloudinit/url_helper.py
@@ -20,21 +20,33 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import httplib
+import json
+import os
+import requests
+import six
import time
-import urllib
-import requests
+from email.utils import parsedate
+from functools import partial
from requests import exceptions
+import oauthlib.oauth1 as oauth1
-from urlparse import (urlparse, urlunparse)
+from six.moves.urllib.parse import (
+ urlparse, urlunparse,
+ quote as urlquote)
from cloudinit import log as logging
from cloudinit import version
LOG = logging.getLogger(__name__)
-NOT_FOUND = httplib.NOT_FOUND
+if six.PY2:
+ import httplib
+ NOT_FOUND = httplib.NOT_FOUND
+else:
+ import http.client
+ NOT_FOUND = http.client.NOT_FOUND
+
# Check if requests has ssl support (added in requests >= 0.8.8)
SSL_ENABLED = False
@@ -70,7 +82,7 @@ def combine_url(base, *add_ons):
path = url_parsed[2]
if path and not path.endswith("/"):
path += "/"
- path += urllib.quote(str(add_on), safe="/:")
+ path += urlquote(str(add_on), safe="/:")
url_parsed[2] = path
return urlunparse(url_parsed)
@@ -135,17 +147,18 @@ class UrlResponse(object):
return self._response.status_code
def __str__(self):
- return self.contents
+ return self._response.text
class UrlError(IOError):
- def __init__(self, cause, code=None, headers=None):
+ def __init__(self, cause, code=None, headers=None, url=None):
IOError.__init__(self, str(cause))
self.cause = cause
self.code = code
self.headers = headers
if self.headers is None:
self.headers = {}
+ self.url = url
def _get_ssl_args(url, ssl_details):
@@ -198,10 +211,14 @@ def readurl(url, data=None, timeout=None, retries=0, sec_between=1,
manual_tries = 1
if retries:
manual_tries = max(int(retries) + 1, 1)
- if not headers:
- headers = {
- 'User-Agent': 'Cloud-Init/%s' % (version.version_string()),
- }
+
+ def_headers = {
+ 'User-Agent': 'Cloud-Init/%s' % (version.version_string()),
+ }
+ if headers:
+ def_headers.update(headers)
+ headers = def_headers
+
if not headers_cb:
def _cb(url):
return headers
@@ -235,18 +252,21 @@ def readurl(url, data=None, timeout=None, retries=0, sec_between=1,
# attrs
return UrlResponse(r)
except exceptions.RequestException as e:
- if (isinstance(e, (exceptions.HTTPError))
- and hasattr(e, 'response') # This appeared in v 0.10.8
- and hasattr(e.response, 'status_code')):
+ if (isinstance(e, (exceptions.HTTPError)) and
+ hasattr(e, 'response') and # This appeared in v 0.10.8
+ hasattr(e.response, 'status_code')):
excps.append(UrlError(e, code=e.response.status_code,
- headers=e.response.headers))
+ headers=e.response.headers,
+ url=url))
else:
- excps.append(UrlError(e))
+ excps.append(UrlError(e, url=url))
if SSL_ENABLED and isinstance(e, exceptions.SSLError):
# ssl exceptions are not going to get fixed by waiting a
# few seconds
break
- if exception_cb and not exception_cb(req_args.copy(), excps[-1]):
+ if exception_cb and exception_cb(req_args.copy(), excps[-1]):
+ # if an exception callback was given it should return None
+ # a true-ish value means to break and re-raise the exception
break
if i + 1 < manual_tries and sec_between > 0:
LOG.debug("Please wait %s seconds while we wait to try again",
@@ -313,7 +333,7 @@ def wait_for_url(urls, max_wait=None, timeout=None,
timeout = int((start_time + max_wait) - now)
reason = ""
- e = None
+ url_exc = None
try:
if headers_cb is not None:
headers = headers_cb(url)
@@ -324,18 +344,20 @@ def wait_for_url(urls, max_wait=None, timeout=None,
check_status=False)
if not response.contents:
reason = "empty response [%s]" % (response.code)
- e = UrlError(ValueError(reason),
- code=response.code, headers=response.headers)
+ url_exc = UrlError(ValueError(reason), code=response.code,
+ headers=response.headers, url=url)
elif not response.ok():
reason = "bad status code [%s]" % (response.code)
- e = UrlError(ValueError(reason),
- code=response.code, headers=response.headers)
+ url_exc = UrlError(ValueError(reason), code=response.code,
+ headers=response.headers, url=url)
else:
return url
except UrlError as e:
reason = "request error [%s]" % e
+ url_exc = e
except Exception as e:
reason = "unexpected error [%s]" % e
+ url_exc = e
time_taken = int(time.time() - start_time)
status_msg = "Calling '%s' failed [%s/%ss]: %s" % (url,
@@ -347,7 +369,7 @@ def wait_for_url(urls, max_wait=None, timeout=None,
# This can be used to alter the headers that will be sent
# in the future, for example this is what the MAAS datasource
# does.
- exception_cb(msg=status_msg, exception=e)
+ exception_cb(msg=status_msg, exception=url_exc)
if timeup(max_wait, start_time):
break
@@ -358,3 +380,129 @@ def wait_for_url(urls, max_wait=None, timeout=None,
time.sleep(sleep_time)
return False
+
+
+class OauthUrlHelper(object):
+ def __init__(self, consumer_key=None, token_key=None,
+ token_secret=None, consumer_secret=None,
+ skew_data_file="/run/oauth_skew.json"):
+ self.consumer_key = consumer_key
+ self.consumer_secret = consumer_secret or ""
+ self.token_key = token_key
+ self.token_secret = token_secret
+ self.skew_data_file = skew_data_file
+ self._do_oauth = True
+ self.skew_change_limit = 5
+ required = (self.token_key, self.token_secret, self.consumer_key)
+ if not any(required):
+ self._do_oauth = False
+ elif not all(required):
+ raise ValueError("all or none of token_key, token_secret, or "
+ "consumer_key can be set")
+
+ old = self.read_skew_file()
+ self.skew_data = old or {}
+
+ def read_skew_file(self):
+ if self.skew_data_file and os.path.isfile(self.skew_data_file):
+ with open(self.skew_data_file, mode="r") as fp:
+ return json.load(fp)
+ return None
+
+ def update_skew_file(self, host, value):
+ # this is not atomic
+ if not self.skew_data_file:
+ return
+ cur = self.read_skew_file()
+ if cur is None:
+ cur = {}
+ cur[host] = value
+ with open(self.skew_data_file, mode="w") as fp:
+ fp.write(json.dumps(cur))
+
+ def exception_cb(self, msg, exception):
+ if not (isinstance(exception, UrlError) and
+ (exception.code == 403 or exception.code == 401)):
+ return
+
+ if 'date' not in exception.headers:
+ LOG.warn("Missing header 'date' in %s response", exception.code)
+ return
+
+ date = exception.headers['date']
+ try:
+ remote_time = time.mktime(parsedate(date))
+ except Exception as e:
+ LOG.warn("Failed to convert datetime '%s': %s", date, e)
+ return
+
+ skew = int(remote_time - time.time())
+ host = urlparse(exception.url).netloc
+ old_skew = self.skew_data.get(host, 0)
+ if abs(old_skew - skew) > self.skew_change_limit:
+ self.update_skew_file(host, skew)
+ LOG.warn("Setting oauth clockskew for %s to %d", host, skew)
+ self.skew_data[host] = skew
+
+ return
+
+ def headers_cb(self, url):
+ if not self._do_oauth:
+ return {}
+
+ timestamp = None
+ host = urlparse(url).netloc
+ if self.skew_data and host in self.skew_data:
+ timestamp = int(time.time()) + self.skew_data[host]
+
+ return oauth_headers(
+ url=url, consumer_key=self.consumer_key,
+ token_key=self.token_key, token_secret=self.token_secret,
+ consumer_secret=self.consumer_secret, timestamp=timestamp)
+
+ def _wrapped(self, wrapped_func, args, kwargs):
+ kwargs['headers_cb'] = partial(
+ self._headers_cb, kwargs.get('headers_cb'))
+ kwargs['exception_cb'] = partial(
+ self._exception_cb, kwargs.get('exception_cb'))
+ return wrapped_func(*args, **kwargs)
+
+ def wait_for_url(self, *args, **kwargs):
+ return self._wrapped(wait_for_url, args, kwargs)
+
+ def readurl(self, *args, **kwargs):
+ return self._wrapped(readurl, args, kwargs)
+
+ def _exception_cb(self, extra_exception_cb, msg, exception):
+ ret = None
+ try:
+ if extra_exception_cb:
+ ret = extra_exception_cb(msg, exception)
+ finally:
+ self.exception_cb(msg, exception)
+ return ret
+
+ def _headers_cb(self, extra_headers_cb, url):
+ headers = {}
+ if extra_headers_cb:
+ headers = extra_headers_cb(url)
+ headers.update(self.headers_cb(url))
+ return headers
+
+
+def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
+ timestamp=None):
+ if timestamp:
+ timestamp = str(timestamp)
+ else:
+ timestamp = None
+
+ client = oauth1.Client(
+ consumer_key,
+ client_secret=consumer_secret,
+ resource_owner_key=token_key,
+ resource_owner_secret=token_secret,
+ signature_method=oauth1.SIGNATURE_PLAINTEXT,
+ timestamp=timestamp)
+ uri, signed_headers, body = client.sign(url)
+ return signed_headers
diff --git a/cloudinit/user_data.py b/cloudinit/user_data.py
index de6487d8..f7c5787c 100644
--- a/cloudinit/user_data.py
+++ b/cloudinit/user_data.py
@@ -22,13 +22,13 @@
import os
-import email
-
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.nonmultipart import MIMENonMultipart
from email.mime.text import MIMEText
+import six
+
from cloudinit import handlers
from cloudinit import log as logging
from cloudinit import util
@@ -49,6 +49,7 @@ INCLUDE_TYPES = ['text/x-include-url', 'text/x-include-once-url']
ARCHIVE_TYPES = ["text/cloud-config-archive"]
UNDEF_TYPE = "text/plain"
ARCHIVE_UNDEF_TYPE = "text/cloud-config"
+ARCHIVE_UNDEF_BINARY_TYPE = "application/octet-stream"
# This seems to hit most of the gzip possible content types.
DECOMP_TYPES = [
@@ -106,7 +107,7 @@ class UserDataProcessor(object):
ctype = None
ctype_orig = part.get_content_type()
- payload = part.get_payload(decode=True)
+ payload = util.fully_decoded_payload(part)
was_compressed = False
# When the message states it is of a gzipped content type ensure
@@ -235,9 +236,9 @@ class UserDataProcessor(object):
resp = util.read_file_or_url(include_url,
ssl_details=self.ssl_details)
if include_once_on and resp.ok():
- util.write_file(include_once_fn, str(resp), mode=0600)
+ util.write_file(include_once_fn, resp.contents, mode=0o600)
if resp.ok():
- content = str(resp)
+ content = resp.contents
else:
LOG.warn(("Fetching from %s resulted in"
" a invalid http code of %s"),
@@ -256,7 +257,7 @@ class UserDataProcessor(object):
# filename and type not be present
# or
# scalar(payload)
- if isinstance(ent, (str, basestring)):
+ if isinstance(ent, six.string_types):
ent = {'content': ent}
if not isinstance(ent, (dict)):
# TODO(harlowja) raise?
@@ -265,11 +266,15 @@ class UserDataProcessor(object):
content = ent.get('content', '')
mtype = ent.get('type')
if not mtype:
- mtype = handlers.type_from_starts_with(content,
- ARCHIVE_UNDEF_TYPE)
+ default = ARCHIVE_UNDEF_TYPE
+ if isinstance(content, six.binary_type):
+ default = ARCHIVE_UNDEF_BINARY_TYPE
+ mtype = handlers.type_from_starts_with(content, default)
maintype, subtype = mtype.split('/', 1)
if maintype == "text":
+ if isinstance(content, six.binary_type):
+ content = content.decode()
msg = MIMEText(content, _subtype=subtype)
else:
msg = MIMEBase(maintype, subtype)
@@ -334,10 +339,10 @@ def convert_string(raw_data, headers=None):
raw_data = ''
if not headers:
headers = {}
- data = util.decomp_gzip(raw_data)
+ data = util.decode_binary(util.decomp_gzip(raw_data))
if "mime-version:" in data[0:4096].lower():
- msg = email.message_from_string(data)
- for (key, val) in headers.iteritems():
+ msg = util.message_from_string(data)
+ for (key, val) in headers.items():
_replace_header(msg, key, val)
else:
mtype = headers.get(CONTENT_TYPE, NOT_MULTIPART_TYPE)
diff --git a/cloudinit/util.py b/cloudinit/util.py
index bf8e7d80..0d21e11b 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -20,11 +20,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from StringIO import StringIO
-
import contextlib
import copy as obj_copy
import ctypes
+import email
import errno
import glob
import grp
@@ -45,8 +44,11 @@ import subprocess
import sys
import tempfile
import time
-import urlparse
+from base64 import b64decode, b64encode
+from six.moves.urllib import parse as urlparse
+
+import six
import yaml
from cloudinit import importer
@@ -69,8 +71,93 @@ FN_REPLACEMENTS = {
}
FN_ALLOWED = ('_-.()' + string.digits + string.ascii_letters)
+TRUE_STRINGS = ('true', '1', 'on', 'yes')
+FALSE_STRINGS = ('off', '0', 'no', 'false')
+
+
# Helper utils to see if running in a container
-CONTAINER_TESTS = ['running-in-container', 'lxc-is-container']
+CONTAINER_TESTS = (['systemd-detect-virt', '--quiet', '--container'],
+ ['running-in-container'],
+ ['lxc-is-container'])
+
+PROC_CMDLINE = None
+
+
+def decode_binary(blob, encoding='utf-8'):
+ # Converts a binary type into a text type using given encoding.
+ if isinstance(blob, six.text_type):
+ return blob
+ return blob.decode(encoding)
+
+
+def encode_text(text, encoding='utf-8'):
+ # Converts a text string into a binary type using given encoding.
+ if isinstance(text, six.binary_type):
+ return text
+ return text.encode(encoding)
+
+
+def b64d(source):
+ # Base64 decode some data, accepting bytes or unicode/str, and returning
+ # str/unicode if the result is utf-8 compatible, otherwise returning bytes.
+ decoded = b64decode(source)
+ try:
+ return decoded.decode('utf-8')
+ except UnicodeDecodeError:
+ return decoded
+
+
+def b64e(source):
+ # Base64 encode some data, accepting bytes or unicode/str, and returning
+ # str/unicode if the result is utf-8 compatible, otherwise returning bytes.
+ if not isinstance(source, bytes):
+ source = source.encode('utf-8')
+ return b64encode(source).decode('utf-8')
+
+
+def fully_decoded_payload(part):
+ # In Python 3, decoding the payload will ironically hand us a bytes object.
+ # 'decode' means to decode according to Content-Transfer-Encoding, not
+ # according to any charset in the Content-Type. So, if we end up with
+ # bytes, first try to decode to str via CT charset, and failing that, try
+ # utf-8 using surrogate escapes.
+ cte_payload = part.get_payload(decode=True)
+ if (six.PY3 and
+ part.get_content_maintype() == 'text' and
+ isinstance(cte_payload, bytes)):
+ charset = part.get_charset()
+ if charset and charset.input_codec:
+ encoding = charset.input_codec
+ else:
+ encoding = 'utf-8'
+ return cte_payload.decode(encoding, errors='surrogateescape')
+ return cte_payload
+
+
+# Path for DMI Data
+DMI_SYS_PATH = "/sys/class/dmi/id"
+
+# dmidecode and /sys/class/dmi/id/* use different names for the same value,
+# this allows us to refer to them by one canonical name
+DMIDECODE_TO_DMI_SYS_MAPPING = {
+ 'baseboard-asset-tag': 'board_asset_tag',
+ 'baseboard-manufacturer': 'board_vendor',
+ 'baseboard-product-name': 'board_name',
+ 'baseboard-serial-number': 'board_serial',
+ 'baseboard-version': 'board_version',
+ 'bios-release-date': 'bios_date',
+ 'bios-vendor': 'bios_vendor',
+ 'bios-version': 'bios_version',
+ 'chassis-asset-tag': 'chassis_asset_tag',
+ 'chassis-manufacturer': 'chassis_vendor',
+ 'chassis-serial-number': 'chassis_serial',
+ 'chassis-version': 'chassis_version',
+ 'system-manufacturer': 'sys_vendor',
+ 'system-product-name': 'product_name',
+ 'system-serial-number': 'product_serial',
+ 'system-uuid': 'product_uuid',
+ 'system-version': 'product_version',
+}
class ProcessExecutionError(IOError):
@@ -95,7 +182,7 @@ class ProcessExecutionError(IOError):
else:
self.description = description
- if not isinstance(exit_code, (long, int)):
+ if not isinstance(exit_code, six.integer_types):
self.exit_code = '-'
else:
self.exit_code = exit_code
@@ -124,6 +211,9 @@ class ProcessExecutionError(IOError):
'reason': self.reason,
}
IOError.__init__(self, message)
+ # For backward compatibility with Python 2.
+ if not hasattr(self, 'message'):
+ self.message = message
class SeLinuxGuard(object):
@@ -151,7 +241,8 @@ class SeLinuxGuard(object):
path = os.path.realpath(self.path)
# path should be a string, not unicode
- path = str(path)
+ if six.PY2:
+ path = str(path)
try:
stats = os.lstat(path)
self.selinux.matchpathcon(path, stats[stat.ST_MODE])
@@ -209,10 +300,10 @@ def fork_cb(child_cb, *args, **kwargs):
def is_true(val, addons=None):
if isinstance(val, (bool)):
return val is True
- check_set = ['true', '1', 'on', 'yes']
+ check_set = TRUE_STRINGS
if addons:
- check_set = check_set + addons
- if str(val).lower().strip() in check_set:
+ check_set = list(check_set) + addons
+ if six.text_type(val).lower().strip() in check_set:
return True
return False
@@ -220,10 +311,10 @@ def is_true(val, addons=None):
def is_false(val, addons=None):
if isinstance(val, (bool)):
return val is False
- check_set = ['off', '0', 'no', 'false']
+ check_set = FALSE_STRINGS
if addons:
- check_set = check_set + addons
- if str(val).lower().strip() in check_set:
+ check_set = list(check_set) + addons
+ if six.text_type(val).lower().strip() in check_set:
return True
return False
@@ -241,7 +332,7 @@ def translate_bool(val, addons=None):
def rand_str(strlen=32, select_from=None):
if not select_from:
- select_from = string.letters + string.digits
+ select_from = string.ascii_letters + string.digits
return "".join([random.choice(select_from) for _x in range(0, strlen)])
@@ -273,7 +364,7 @@ def uniq_merge_sorted(*lists):
def uniq_merge(*lists):
combined_list = []
for a_list in lists:
- if isinstance(a_list, (str, basestring)):
+ if isinstance(a_list, six.string_types):
a_list = a_list.strip().split(",")
# Kickout the empty ones
a_list = [a for a in a_list if len(a)]
@@ -282,7 +373,7 @@ def uniq_merge(*lists):
def clean_filename(fn):
- for (k, v) in FN_REPLACEMENTS.iteritems():
+ for (k, v) in FN_REPLACEMENTS.items():
fn = fn.replace(k, v)
removals = []
for k in fn:
@@ -294,16 +385,19 @@ def clean_filename(fn):
return fn
-def decomp_gzip(data, quiet=True):
+def decomp_gzip(data, quiet=True, decode=True):
try:
- buf = StringIO(str(data))
+ buf = six.BytesIO(encode_text(data))
with contextlib.closing(gzip.GzipFile(None, "rb", 1, buf)) as gh:
- return gh.read()
+ if decode:
+ return decode_binary(gh.read())
+ else:
+ return gh.read()
except Exception as e:
if quiet:
return data
else:
- raise DecompressionError(str(e))
+ raise DecompressionError(six.text_type(e))
def extract_usergroup(ug_pair):
@@ -341,7 +435,7 @@ def multi_log(text, console=True, stderr=True,
if console:
conpath = "/dev/console"
if os.path.exists(conpath):
- with open(conpath, 'wb') as wfh:
+ with open(conpath, 'w') as wfh:
wfh.write(text)
wfh.flush()
else:
@@ -362,7 +456,7 @@ def multi_log(text, console=True, stderr=True,
def load_json(text, root_types=(dict,)):
- decoded = json.loads(text)
+ decoded = json.loads(decode_binary(text))
if not isinstance(decoded, tuple(root_types)):
expected_types = ", ".join([str(t) for t in root_types])
raise TypeError("(%s) root types expected, got %s instead"
@@ -394,7 +488,7 @@ def get_cfg_option_str(yobj, key, default=None):
if key not in yobj:
return default
val = yobj[key]
- if not isinstance(val, (str, basestring)):
+ if not isinstance(val, six.string_types):
val = str(val)
return val
@@ -433,7 +527,7 @@ def get_cfg_option_list(yobj, key, default=None):
if isinstance(val, (list)):
cval = [v for v in val]
return cval
- if not isinstance(val, (basestring)):
+ if not isinstance(val, six.string_types):
val = str(val)
return [val]
@@ -520,7 +614,7 @@ def redirect_output(outfmt, errfmt, o_out=None, o_err=None):
def make_url(scheme, host, port=None,
- path='', params='', query='', fragment=''):
+ path='', params='', query='', fragment=''):
pieces = []
pieces.append(scheme or '')
@@ -687,12 +781,13 @@ def read_file_or_url(url, timeout=5, retries=10,
LOG.warn("Unable to post data to file resource %s", url)
file_path = url[len("file://"):]
try:
- contents = load_file(file_path)
+ contents = load_file(file_path, decode=False)
except IOError as e:
code = e.errno
if e.errno == errno.ENOENT:
code = url_helper.NOT_FOUND
- raise url_helper.UrlError(cause=e, code=code, headers=None)
+ raise url_helper.UrlError(cause=e, code=code, headers=None,
+ url=url)
return url_helper.FileResponse(file_path, contents=contents)
else:
return url_helper.readurl(url,
@@ -708,11 +803,11 @@ def read_file_or_url(url, timeout=5, retries=10,
def load_yaml(blob, default=None, allowed=(dict,)):
loaded = default
+ blob = decode_binary(blob)
try:
- blob = str(blob)
- LOG.debug(("Attempting to load yaml from string "
- "of length %s with allowed root types %s"),
- len(blob), allowed)
+ LOG.debug("Attempting to load yaml from string "
+ "of length %s with allowed root types %s",
+ len(blob), allowed)
converted = safeyaml.load(blob)
if not isinstance(converted, allowed):
# Yes this will just be caught, but thats ok for now...
@@ -746,14 +841,12 @@ def read_seeded(base="", ext="", timeout=5, retries=10, file_retries=0):
md_resp = read_file_or_url(md_url, timeout, retries, file_retries)
md = None
if md_resp.ok():
- md_str = str(md_resp)
- md = load_yaml(md_str, default={})
+ md = load_yaml(decode_binary(md_resp.contents), default={})
ud_resp = read_file_or_url(ud_url, timeout, retries, file_retries)
ud = None
if ud_resp.ok():
- ud_str = str(ud_resp)
- ud = ud_str
+ ud = ud_resp.contents
return (md, ud)
@@ -784,10 +877,10 @@ def read_conf_with_confd(cfgfile):
if "conf_d" in cfg:
confd = cfg['conf_d']
if confd:
- if not isinstance(confd, (str, basestring)):
+ if not isinstance(confd, six.string_types):
raise TypeError(("Config file %s contains 'conf_d' "
"with non-string type %s") %
- (cfgfile, type_utils.obj_name(confd)))
+ (cfgfile, type_utils.obj_name(confd)))
else:
confd = str(confd).strip()
elif os.path.isdir("%s.d" % cfgfile):
@@ -905,7 +998,7 @@ def get_fqdn_from_hosts(hostname, filename="/etc/hosts"):
def get_cmdline_url(names=('cloud-config-url', 'url'),
- starts="#cloud-config", cmdline=None):
+ starts=b"#cloud-config", cmdline=None):
if cmdline is None:
cmdline = get_cmdline()
@@ -921,8 +1014,10 @@ def get_cmdline_url(names=('cloud-config-url', 'url'),
return (None, None, None)
resp = read_file_or_url(url)
- if resp.contents.startswith(starts) and resp.ok():
- return (key, url, str(resp))
+ # allow callers to pass starts as text when comparing to bytes contents
+ starts = encode_text(starts)
+ if resp.ok() and resp.contents.startswith(starts):
+ return (key, url, resp.contents)
return (key, url, None)
@@ -948,7 +1043,8 @@ def is_resolvable(name):
for iname in badnames:
try:
result = socket.getaddrinfo(iname, None, 0, 0,
- socket.SOCK_STREAM, socket.AI_CANONNAME)
+ socket.SOCK_STREAM,
+ socket.AI_CANONNAME)
badresults[iname] = []
for (_fam, _stype, _proto, cname, sockaddr) in result:
badresults[iname].append("%s: %s" % (cname, sockaddr[0]))
@@ -1016,7 +1112,7 @@ def close_stdin():
def find_devs_with(criteria=None, oformat='device',
- tag=None, no_cache=False, path=None):
+ tag=None, no_cache=False, path=None):
"""
find devices matching given criteria (via blkid)
criteria can be *one* of:
@@ -1076,9 +1172,9 @@ def uniq_list(in_list):
return out_list
-def load_file(fname, read_cb=None, quiet=False):
+def load_file(fname, read_cb=None, quiet=False, decode=True):
LOG.debug("Reading from %s (quiet=%s)", fname, quiet)
- ofh = StringIO()
+ ofh = six.BytesIO()
try:
with open(fname, 'rb') as ifh:
pipe_in_out(ifh, ofh, chunk_cb=read_cb)
@@ -1089,17 +1185,35 @@ def load_file(fname, read_cb=None, quiet=False):
raise
contents = ofh.getvalue()
LOG.debug("Read %s bytes from %s", len(contents), fname)
- return contents
+ if decode:
+ return decode_binary(contents)
+ else:
+ return contents
def get_cmdline():
if 'DEBUG_PROC_CMDLINE' in os.environ:
- cmdline = os.environ["DEBUG_PROC_CMDLINE"]
+ return os.environ["DEBUG_PROC_CMDLINE"]
+
+ global PROC_CMDLINE
+ if PROC_CMDLINE is not None:
+ return PROC_CMDLINE
+
+ if is_container():
+ try:
+ contents = load_file("/proc/1/cmdline")
+ # replace nulls with space and drop trailing null
+ cmdline = contents.replace("\x00", " ")[:-1]
+ except Exception as e:
+ LOG.warn("failed reading /proc/1/cmdline: %s", e)
+ cmdline = ""
else:
try:
cmdline = load_file("/proc/cmdline").strip()
except:
cmdline = ""
+
+ PROC_CMDLINE = cmdline
return cmdline
@@ -1107,7 +1221,7 @@ def pipe_in_out(in_fh, out_fh, chunk_size=1024, chunk_cb=None):
bytes_piped = 0
while True:
data = in_fh.read(chunk_size)
- if data == '':
+ if len(data) == 0:
break
else:
out_fh.write(data)
@@ -1213,13 +1327,20 @@ def logexc(log, msg, *args):
# coming out to a non-debug stream
if msg:
log.warn(msg, *args)
- # Debug gets the full trace
- log.debug(msg, exc_info=1, *args)
+ # Debug gets the full trace. However, nose has a bug whereby its
+ # logcapture plugin doesn't properly handle the case where there is no
+ # actual exception. To avoid tracebacks during the test suite then, we'll
+ # do the actual exc_info extraction here, and if there is no exception in
+ # flight, we'll just pass in None.
+ exc_info = sys.exc_info()
+ if exc_info == (None, None, None):
+ exc_info = None
+ log.debug(msg, exc_info=exc_info, *args)
def hash_blob(blob, routine, mlen=None):
hasher = hashlib.new(routine)
- hasher.update(blob)
+ hasher.update(encode_text(blob))
digest = hasher.hexdigest()
# Don't get to long now
if mlen is not None:
@@ -1250,7 +1371,7 @@ def rename(src, dest):
os.rename(src, dest)
-def ensure_dirs(dirlist, mode=0755):
+def ensure_dirs(dirlist, mode=0o755):
for d in dirlist:
ensure_dir(d, mode)
@@ -1264,7 +1385,7 @@ def read_write_cmdline_url(target_fn):
return
try:
if key and content:
- write_file(target_fn, content, mode=0600)
+ write_file(target_fn, content, mode=0o600)
LOG.debug(("Wrote to %s with contents of command line"
" url %s (len=%s)"), target_fn, url, len(content))
elif key and not content:
@@ -1280,8 +1401,7 @@ def yaml_dumps(obj, explicit_start=True, explicit_end=True):
indent=4,
explicit_start=explicit_start,
explicit_end=explicit_end,
- default_flow_style=False,
- allow_unicode=True)
+ default_flow_style=False)
def ensure_dir(path, mode=None):
@@ -1380,9 +1500,10 @@ def mount_cb(device, callback, data=None, rw=False, mtype=None, sync=True):
mounted = mounts()
with tempdir() as tmpd:
umount = False
- if device in mounted:
- mountpoint = mounted[device]['mountpoint']
+ if os.path.realpath(device) in mounted:
+ mountpoint = mounted[os.path.realpath(device)]['mountpoint']
else:
+ failure_reason = None
for mtype in mtypes:
mountpoint = None
try:
@@ -1409,10 +1530,10 @@ def mount_cb(device, callback, data=None, rw=False, mtype=None, sync=True):
except (IOError, OSError) as exc:
LOG.debug("Failed mount of '%s' as '%s': %s",
device, mtype, exc)
- pass
+ failure_reason = exc
if not mountpoint:
raise MountFailedError("Failed mounting %s to %s due to: %s" %
- (device, tmpd, exc))
+ (device, tmpd, failure_reason))
# Be nice and ensure it ends with a slash
if not mountpoint.endswith("/"):
@@ -1465,7 +1586,7 @@ def uptime():
try:
if os.path.exists("/proc/uptime"):
method = '/proc/uptime'
- contents = load_file("/proc/uptime").strip()
+ contents = load_file("/proc/uptime")
if contents:
uptime_str = contents.split()[0]
else:
@@ -1489,7 +1610,7 @@ def append_file(path, content):
write_file(path, content, omode="ab", mode=None)
-def ensure_file(path, mode=0644):
+def ensure_file(path, mode=0o644):
write_file(path, content='', omode="ab", mode=mode)
@@ -1507,7 +1628,7 @@ def chmod(path, mode):
os.chmod(path, real_mode)
-def write_file(filename, content, mode=0644, omode="wb"):
+def write_file(filename, content, mode=0o644, omode="wb"):
"""
Writes a file with the given content and sets the file mode as specified.
Resotres the SELinux context if possible.
@@ -1515,11 +1636,17 @@ def write_file(filename, content, mode=0644, omode="wb"):
@param filename: The full path of the file to write.
@param content: The content to write to the file.
@param mode: The filesystem mode to set on the file.
- @param omode: The open mode used when opening the file (r, rb, a, etc.)
+ @param omode: The open mode used when opening the file (w, wb, a, etc.)
"""
ensure_dir(os.path.dirname(filename))
- LOG.debug("Writing to %s - %s: [%s] %s bytes",
- filename, omode, mode, len(content))
+ if 'b' in omode.lower():
+ content = encode_text(content)
+ write_type = 'bytes'
+ else:
+ content = decode_binary(content)
+ write_type = 'characters'
+ LOG.debug("Writing to %s - %s: [%s] %s %s",
+ filename, omode, mode, len(content), write_type)
with SeLinuxGuard(path=filename):
with open(filename, omode) as fh:
fh.write(content)
@@ -1561,9 +1688,12 @@ def subp(args, data=None, rcs=None, env=None, capture=True, shell=False,
stdout = subprocess.PIPE
stderr = subprocess.PIPE
stdin = subprocess.PIPE
- sp = subprocess.Popen(args, stdout=stdout,
- stderr=stderr, stdin=stdin,
- env=env, shell=shell)
+ kws = dict(stdout=stdout, stderr=stderr, stdin=stdin,
+ env=env, shell=shell)
+ if six.PY3:
+ # Use this so subprocess output will be (Python 3) str, not bytes.
+ kws['universal_newlines'] = True
+ sp = subprocess.Popen(args, **kws)
(out, err) = sp.communicate(data)
except OSError as e:
raise ProcessExecutionError(cmd=args, reason=e)
@@ -1608,10 +1738,10 @@ def shellify(cmdlist, add_header=True):
if isinstance(args, list):
fixed = []
for f in args:
- fixed.append("'%s'" % (str(f).replace("'", escaped)))
+ fixed.append("'%s'" % (six.text_type(f).replace("'", escaped)))
content = "%s%s\n" % (content, ' '.join(fixed))
cmds_made += 1
- elif isinstance(args, (str, basestring)):
+ elif isinstance(args, six.string_types):
content = "%s%s\n" % (content, args)
cmds_made += 1
else:
@@ -1639,7 +1769,7 @@ def is_container():
try:
# try to run a helper program. if it returns true/zero
# then we're inside a container. otherwise, no
- subp([helper])
+ subp(helper)
return True
except (IOError, OSError):
pass
@@ -1722,7 +1852,7 @@ def expand_package_list(version_fmt, pkgs):
pkglist = []
for pkg in pkgs:
- if isinstance(pkg, basestring):
+ if isinstance(pkg, six.string_types):
pkglist.append(pkg)
continue
@@ -1950,7 +2080,7 @@ def pathprefix2dict(base, required=None, optional=None, delim=os.path.sep):
ret = {}
for f in required + optional:
try:
- ret[f] = load_file(base + delim + f, quiet=False)
+ ret[f] = load_file(base + delim + f, quiet=False, decode=False)
except IOError as e:
if e.errno != errno.ENOENT:
raise
@@ -2011,3 +2141,86 @@ def human2bytes(size):
raise ValueError("'%s': cannot be negative" % size_in)
return int(num * mpliers[mplier])
+
+
+def _read_dmi_syspath(key):
+ """
+ Reads dmi data with from /sys/class/dmi/id
+ """
+ if key not in DMIDECODE_TO_DMI_SYS_MAPPING:
+ return None
+ mapped_key = DMIDECODE_TO_DMI_SYS_MAPPING[key]
+ dmi_key_path = "{0}/{1}".format(DMI_SYS_PATH, mapped_key)
+ LOG.debug("querying dmi data %s", dmi_key_path)
+ try:
+ if not os.path.exists(dmi_key_path):
+ LOG.debug("did not find %s", dmi_key_path)
+ return None
+
+ key_data = load_file(dmi_key_path, decode=False)
+ if not key_data:
+ LOG.debug("%s did not return any data", dmi_key_path)
+ return None
+
+ # uninitialized dmi values show as all \xff and /sys appends a '\n'.
+ # in that event, return a string of '.' in the same length.
+ if key_data == b'\xff' * (len(key_data) - 1) + b'\n':
+ key_data = b""
+
+ str_data = key_data.decode('utf8').strip()
+ LOG.debug("dmi data %s returned %s", dmi_key_path, str_data)
+ return str_data
+
+ except Exception:
+ logexc(LOG, "failed read of %s", dmi_key_path)
+ return None
+
+
+def _call_dmidecode(key, dmidecode_path):
+ """
+ Calls out to dmidecode to get the data out. This is mostly for supporting
+ OS's without /sys/class/dmi/id support.
+ """
+ try:
+ cmd = [dmidecode_path, "--string", key]
+ (result, _err) = subp(cmd)
+ LOG.debug("dmidecode returned '%s' for '%s'", result, key)
+ result = result.strip()
+ if result.replace(".", "") == "":
+ return ""
+ return result
+ except (IOError, OSError) as _err:
+ LOG.debug('failed dmidecode cmd: %s\n%s', cmd, _err.message)
+ return None
+
+
+def read_dmi_data(key):
+ """
+ Wrapper for reading DMI data.
+
+ This will do the following (returning the first that produces a
+ result):
+ 1) Use a mapping to translate `key` from dmidecode naming to
+ sysfs naming and look in /sys/class/dmi/... for a value.
+ 2) Use `key` as a sysfs key directly and look in /sys/class/dmi/...
+ 3) Fall-back to passing `key` to `dmidecode --string`.
+
+ If all of the above fail to find a value, None will be returned.
+ """
+ syspath_value = _read_dmi_syspath(key)
+ if syspath_value is not None:
+ return syspath_value
+
+ dmidecode_path = which('dmidecode')
+ if dmidecode_path:
+ return _call_dmidecode(key, dmidecode_path)
+
+ LOG.warn("did not find either path %s or dmidecode command",
+ DMI_SYS_PATH)
+ return None
+
+
+def message_from_string(string):
+ if sys.version_info[:2] < (2, 7):
+ return email.message_from_file(six.StringIO(string))
+ return email.message_from_string(string)
diff --git a/config/cloud.cfg b/config/cloud.cfg
index 200050d3..a6afcc83 100644
--- a/config/cloud.cfg
+++ b/config/cloud.cfg
@@ -48,12 +48,15 @@ cloud_config_modules:
- ssh-import-id
- locale
- set-passwords
+ - snappy
- grub-dpkg
- apt-pipelining
- apt-configure
- package-update-upgrade-install
+ - fan
- landscape
- timezone
+ - lxd
- puppet
- chef
- salt-minion
@@ -86,7 +89,7 @@ system_info:
name: ubuntu
lock_passwd: True
gecos: Ubuntu
- groups: [adm, audio, cdrom, dialout, dip, floppy, netdev, plugdev, sudo, video]
+ groups: [adm, audio, cdrom, dialout, dip, floppy, lxd, netdev, plugdev, sudo, video]
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
shell: /bin/bash
# Other config here will be given to the distro class and/or path classes
@@ -103,6 +106,7 @@ system_info:
primary:
- http://%(ec2_region)s.ec2.archive.ubuntu.com/ubuntu/
- http://%(availability_zone)s.clouds.archive.ubuntu.com/ubuntu/
+ - http://%(region)s.clouds.archive.ubuntu.com/ubuntu/
security: []
- arches: [armhf, armel, default]
failsafe:
diff --git a/doc/examples/cloud-config-datasources.txt b/doc/examples/cloud-config-datasources.txt
index 3bde4aac..2651c027 100644
--- a/doc/examples/cloud-config-datasources.txt
+++ b/doc/examples/cloud-config-datasources.txt
@@ -51,12 +51,19 @@ datasource:
policy: on # [can be 'on', 'off' or 'force']
SmartOS:
+ # For KVM guests:
# Smart OS datasource works over a serial console interacting with
# a server on the other end. By default, the second serial console is the
# device. SmartOS also uses a serial timeout of 60 seconds.
serial_device: /dev/ttyS1
serial_timeout: 60
+ # For LX-Brand Zones guests:
+ # Smart OS datasource works over a socket interacting with
+ # the host on the other end. By default, the socket file is in
+ # the native .zoncontrol directory.
+ metadata_sockfile: /native/.zonecontrol/metadata.sock
+
# a list of keys that will not be base64 decoded even if base64_all
no_base64_decode: ['root_authorized_keys', 'motd_sys_info',
'iptables_disable']
diff --git a/doc/examples/cloud-config-lxd.txt b/doc/examples/cloud-config-lxd.txt
new file mode 100644
index 00000000..b9bb4aa5
--- /dev/null
+++ b/doc/examples/cloud-config-lxd.txt
@@ -0,0 +1,28 @@
+#cloud-config
+
+# configure lxd
+# default: none
+# all options default to none if not specified
+# lxd: config sections for lxd
+# init: dict of options for lxd init, see 'man lxd'
+# network_address: address for lxd to listen on
+# network_port: port for lxd to listen on
+# storage_backend: either 'zfs' or 'dir'
+# storage_create_device: device based storage using specified device
+# storage_create_loop: set up loop based storage with size in GB
+# storage_pool: name of storage pool to use or create
+# trust_password: password required to add new clients
+
+lxd:
+ init:
+ network_address: 0.0.0.0
+ network_port: 8443
+ storage_backend: zfs
+ storage_pool: datapool
+ storage_create_loop: 10
+
+
+# The simplist working configuration is
+# lxd:
+# init:
+# storage_backend: dir
diff --git a/doc/examples/cloud-config-mount-points.txt b/doc/examples/cloud-config-mount-points.txt
index 3b45b47f..aa676c24 100644
--- a/doc/examples/cloud-config-mount-points.txt
+++ b/doc/examples/cloud-config-mount-points.txt
@@ -42,5 +42,5 @@ mount_default_fields: [ None, None, "auto", "defaults,nobootwait", "0", "2" ]
# default is to not create any swap files, because 'size' is set to 0
swap:
filename: /swap.img
- size: "auto" or size in bytes
+ size: "auto" # or size in bytes
maxsize: size in bytes
diff --git a/doc/examples/cloud-config-power-state.txt b/doc/examples/cloud-config-power-state.txt
index 8df14366..b470153d 100644
--- a/doc/examples/cloud-config-power-state.txt
+++ b/doc/examples/cloud-config-power-state.txt
@@ -23,9 +23,18 @@
# message: provided as the message argument to 'shutdown'. default is none.
# timeout: the amount of time to give the cloud-init process to finish
# before executing shutdown.
+# condition: apply state change only if condition is met.
+# May be boolean True (always met), or False (never met),
+# or a command string or list to be executed.
+# command's exit code indicates:
+# 0: condition met
+# 1: condition not met
+# other exit codes will result in 'not met', but are reserved
+# for future use.
#
power_state:
delay: "+30"
mode: poweroff
message: Bye Bye
timeout: 30
+ condition: True
diff --git a/doc/examples/cloud-config-reporting.txt b/doc/examples/cloud-config-reporting.txt
new file mode 100644
index 00000000..ee00078f
--- /dev/null
+++ b/doc/examples/cloud-config-reporting.txt
@@ -0,0 +1,17 @@
+#cloud-config
+##
+## The following sets up 2 reporting end points.
+## A 'webhook' and a 'log' type.
+## It also disables the built in default 'log'
+reporting:
+ smtest:
+ type: webhook
+ endpoint: "http://myhost:8000/"
+ consumer_key: "ckey_foo"
+ consumer_secret: "csecret_foo"
+ token_key: "tkey_foo"
+ token_secret: "tkey_foo"
+ smlogger:
+ type: log
+ level: WARN
+ log: null
diff --git a/doc/examples/cloud-config-rh_subscription.txt b/doc/examples/cloud-config-rh_subscription.txt
new file mode 100644
index 00000000..be121338
--- /dev/null
+++ b/doc/examples/cloud-config-rh_subscription.txt
@@ -0,0 +1,49 @@
+#cloud-config
+
+# register your Red Hat Enterprise Linux based operating system
+#
+# this cloud-init plugin is capable of registering by username
+# and password *or* activation and org. Following a successfully
+# registration you can:
+# - auto-attach subscriptions
+# - set the service level
+# - add subscriptions based on its pool ID
+# - enable yum repositories based on its repo id
+# - disable yum repositories based on its repo id
+# - alter the rhsm_baseurl and server-hostname in the
+# /etc/rhsm/rhs.conf file
+
+rh_subscription:
+ username: joe@foo.bar
+
+ ## Quote your password if it has symbols to be safe
+ password: '1234abcd'
+
+ ## If you prefer, you can use the activation key and
+ ## org instead of username and password. Be sure to
+ ## comment out username and password
+
+ #activation-key: foobar
+ #org: 12345
+
+ ## Uncomment to auto-attach subscriptions to your system
+ #auto-attach: True
+
+ ## Uncomment to set the service level for your
+ ## subscriptions
+ #service-level: self-support
+
+ ## Uncomment to add pools (needs to be a list of IDs)
+ #add-pool: []
+
+ ## Uncomment to add or remove yum repos
+ ## (needs to be a list of repo IDs)
+ #enable-repo: []
+ #disable-repo: []
+
+ ## Uncomment to alter the baseurl in /etc/rhsm/rhsm.conf
+ #rhsm-baseurl: http://url
+
+ ## Uncomment to alter the server hostname in
+ ## /etc/rhsm/rhsm.conf
+ #server-hostname: foo.bar.com
diff --git a/doc/examples/cloud-config-rsyslog.txt b/doc/examples/cloud-config-rsyslog.txt
new file mode 100644
index 00000000..28ea1f16
--- /dev/null
+++ b/doc/examples/cloud-config-rsyslog.txt
@@ -0,0 +1,46 @@
+## the rsyslog module allows you to configure the systems syslog.
+## configuration of syslog is under the top level cloud-config
+## entry 'rsyslog'.
+##
+## Example:
+#cloud-config
+rsyslog:
+ remotes:
+ # udp to host 'maas.mydomain' port 514
+ maashost: maas.mydomain
+ # udp to ipv4 host on port 514
+ maas: "@[10.5.1.56]:514"
+ # tcp to host ipv6 host on port 555
+ maasipv6: "*.* @@[FE80::0202:B3FF:FE1E:8329]:555"
+ configs:
+ - "*.* @@192.158.1.1"
+ - content: "*.* @@192.0.2.1:10514"
+ filename: 01-example.conf
+ - content: |
+ *.* @@syslogd.example.com
+ config_dir: /etc/rsyslog.d
+ config_filename: 20-cloud-config.conf
+ service_reload_command: [your, syslog, reload, command]
+
+## Additionally the following legacy format is supported
+## it is converted into the format above before use.
+## rsyslog_filename -> rsyslog/config_filename
+## rsyslog_dir -> rsyslog/config_dir
+## rsyslog -> rsyslog/configs
+# rsyslog:
+# - "*.* @@192.158.1.1"
+# - content: "*.* @@192.0.2.1:10514"
+# filename: 01-example.conf
+# - content: |
+# *.* @@syslogd.example.com
+# rsyslog_filename: 20-cloud-config.conf
+# rsyslog_dir: /etc/rsyslog.d
+
+## to configure rsyslog to accept remote logging on Ubuntu
+## write the following into /etc/rsyslog.d/20-remote-udp.conf
+## $ModLoad imudp
+## $UDPServerRun 514
+## $template LogRemote,"/var/log/maas/rsyslog/%HOSTNAME%/messages"
+## :fromhost-ip, !isequal, "127.0.0.1" ?LogRemote
+## then:
+## sudo service rsyslog restart
diff --git a/doc/examples/cloud-config-seed-random.txt b/doc/examples/cloud-config-seed-random.txt
new file mode 100644
index 00000000..08f69a9f
--- /dev/null
+++ b/doc/examples/cloud-config-seed-random.txt
@@ -0,0 +1,32 @@
+#cloud-config
+#
+# random_seed is a dictionary.
+#
+# The config module will write seed data from the datasource
+# to 'file' described below.
+#
+# Entries in this dictionary are:
+# file: the file to write random data to (default is /dev/urandom)
+# data: this data will be written to 'file' before data from
+# the datasource
+# encoding: this will be used to decode 'data' provided.
+# allowed values are 'encoding', 'raw', 'base64', 'b64'
+# 'gzip', or 'gz'. Default is 'raw'
+#
+# command: execute this command to seed random.
+# the command will have RANDOM_SEED_FILE in its environment
+# set to the value of 'file' above.
+# command_required: default False
+# if true, and 'command' is not available to be run
+# then exception is raised and cloud-init will record failure.
+# Otherwise, only debug error is mentioned.
+#
+# Note: command could be ['pollinate',
+# '--server=http://local.pollinate.server']
+# which would have pollinate populate /dev/urandom from provided server
+seed_random:
+ file: '/dev/urandom'
+ data: 'my random string'
+ encoding: 'raw'
+ command: ['sh', '-c', 'dd if=/dev/urandom of=$RANDOM_SEED_FILE']
+ command_required: True
diff --git a/doc/examples/cloud-config-user-groups.txt b/doc/examples/cloud-config-user-groups.txt
index 31491faf..0e8ed243 100644
--- a/doc/examples/cloud-config-user-groups.txt
+++ b/doc/examples/cloud-config-user-groups.txt
@@ -15,14 +15,14 @@ users:
selinux-user: staff_u
expiredate: 2012-09-01
ssh-import-id: foobar
- lock-passwd: false
+ lock_passwd: false
passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/
- name: barfoo
gecos: Bar B. Foo
sudo: ALL=(ALL) NOPASSWD:ALL
groups: users, admin
ssh-import-id: None
- lock-passwd: true
+ lock_passwd: true
ssh-authorized-keys:
- <ssh pub key 1>
- <ssh pub key 2>
@@ -42,7 +42,7 @@ users:
# selinux-user: Optional. The SELinux user for the user's login, such as
# "staff_u". When this is omitted the system will select the default
# SELinux user.
-# lock-passwd: Defaults to true. Lock the password to disable password login
+# lock_passwd: Defaults to true. Lock the password to disable password login
# inactive: Create the user as inactive
# passwd: The hash -- not the password itself -- of the password you want
# to use for this user. You can generate a safe hash via:
diff --git a/doc/examples/cloud-config.txt b/doc/examples/cloud-config.txt
index ed4eb7fc..1236796c 100644
--- a/doc/examples/cloud-config.txt
+++ b/doc/examples/cloud-config.txt
@@ -484,7 +484,9 @@ resize_rootfs: True
# final_message
# default: cloud-init boot finished at $TIMESTAMP. Up $UPTIME seconds
# this message is written by cloud-final when the system is finished
-# its first boot
+# its first boot.
+# This message is rendered as if it were a template. If you
+# want jinja, you have to start the line with '## template:jinja\n'
final_message: "The system is finally up, after $UPTIME seconds"
# configure where output will go
@@ -534,6 +536,8 @@ timezone: US/Eastern
#
# to remedy this situation, 'def_log_file' can be set to a filename
# and syslog_fix_perms to a string containing "<user>:<group>"
+# if syslog_fix_perms is a list, it will iterate through and use the
+# first pair that does not raise error.
#
# the default values are '/var/log/cloud-init.log' and 'syslog:adm'
# the value of 'def_log_file' should match what is configured in logging
diff --git a/doc/rtd/conf.py b/doc/rtd/conf.py
index d3764bea..8a391f21 100644
--- a/doc/rtd/conf.py
+++ b/doc/rtd/conf.py
@@ -68,8 +68,8 @@ html_theme = 'default'
# further. For a list of options available for each theme, see the
# documentation.
html_theme_options = {
- "bodyfont": "Arial, sans-serif",
- "headfont": "Arial, sans-serif"
+ "bodyfont": "Ubuntu, Arial, sans-serif",
+ "headfont": "Ubuntu, Arial, sans-serif"
}
# The name of an image file (relative to this directory) to place at the top
diff --git a/doc/rtd/static/logo.png b/doc/rtd/static/logo.png
index 893b7e3b..e980fdea 100644
--- a/doc/rtd/static/logo.png
+++ b/doc/rtd/static/logo.png
Binary files differ
diff --git a/doc/rtd/static/logo.svg b/doc/rtd/static/logo.svg
index b22ce2a0..7a2ae21b 100644
--- a/doc/rtd/static/logo.svg
+++ b/doc/rtd/static/logo.svg
@@ -1,14356 +1,89 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="744.09448819"
- height="1052.3622047"
- id="svg2"
- version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="New document 1">
- <defs
- id="defs4">
- <linearGradient
- y2="70.436849"
- y1="59.780132"
- xlink:href="#linearGradient838"
- x2="72.315912"
- x1="62.94737"
- id="linearGradient931"
- gradientTransform="scale(1.4416968,0.69362714)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- y2="82.421027"
- y1="65.830238"
- xlink:href="#linearGradient838"
- x2="31.072908"
- x1="23.497205"
- id="linearGradient929"
- gradientTransform="scale(1.6488961,0.60646635)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- y2="23.741014"
- y1="15.459764"
- xlink:href="#linearGradient838"
- x2="134.75218"
- x1="126.70531"
- id="linearGradient837"
- gradientUnits="userSpaceOnUse" />
- <radialGradient
- xlink:href="#linearGradient827"
- r="38.94874"
- id="radialGradient830"
- fy="49.007641"
- fx="76.033927"
- cy="48.244743"
- cx="75.282185"
- gradientTransform="scale(1.0155474,0.98469063)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient827">
- <stop
- style="stop-color:#ffffff;stop-opacity:1.0000000;"
- offset="0.0000000"
- id="stop828" />
- <stop
- style="stop-color:#ffc613;stop-opacity:1.0000000;"
- offset="1.0000000"
- id="stop829" />
- </linearGradient>
- <linearGradient
- id="linearGradient838">
- <stop
- style="stop-color:#ffffff;stop-opacity:0.50000000;"
- offset="0.0000000"
- id="stop839" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0.0000000;"
- offset="1.0000000"
- id="stop840" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient838"
- id="linearGradient3072"
- x1="126.70531"
- y1="15.459764"
- x2="134.75218"
- y2="23.741014"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient4542">
- <stop
- id="stop4544"
- offset="0"
- style="stop-color:#4879c2;stop-opacity:1;" />
- <stop
- id="stop4546"
- offset="1"
- style="stop-color:#f5f8fc;stop-opacity:1;" />
- </linearGradient>
- <linearGradient
- id="linearGradient4528">
- <stop
- style="stop-color:#c5d9e3;stop-opacity:1;"
- offset="0"
- id="stop4530" />
- <stop
- style="stop-color:#3b6bb1;stop-opacity:1"
- offset="1"
- id="stop4532" />
- </linearGradient>
- <linearGradient
- id="linearGradient4534">
- <stop
- id="stop4536"
- offset="0"
- style="stop-color:#c7e0ec;stop-opacity:1;" />
- <stop
- id="stop4538"
- offset="1"
- style="stop-color:#3b6bb1;stop-opacity:1" />
- </linearGradient>
- <linearGradient
- id="linearGradient3236">
- <stop
- id="stop3238"
- offset="0"
- style="stop-color:#cccccc;stop-opacity:1;" />
- <stop
- id="stop3240"
- offset="1"
- style="stop-color:#f6f6f6;stop-opacity:1;" />
- </linearGradient>
- <linearGradient
- id="linearGradient6013">
- <stop
- style="stop-color:#260c72;stop-opacity:1;"
- offset="0"
- id="stop6015" />
- <stop
- style="stop-color:#c8bfef;stop-opacity:1;"
- offset="1"
- id="stop6017" />
- </linearGradient>
- <linearGradient
- id="linearGradient5997">
- <stop
- id="stop5999"
- offset="0"
- style="stop-color:#706985;stop-opacity:1;" />
- <stop
- id="stop6001"
- offset="1"
- style="stop-color:#c8bfef;stop-opacity:1;" />
- </linearGradient>
- <linearGradient
- id="linearGradient3262">
- <stop
- id="stop3264"
- offset="0"
- style="stop-color:#b8d9e6;stop-opacity:1;" />
- <stop
- id="stop3266"
- offset="1"
- style="stop-color:#2669a9;stop-opacity:1;" />
- </linearGradient>
- <linearGradient
- id="linearGradient3186">
- <stop
- id="stop3188"
- offset="0"
- style="stop-color:#c3b08a;stop-opacity:1;" />
- <stop
- id="stop3190"
- offset="1"
- style="stop-color:#e7dfd1;stop-opacity:1;" />
- </linearGradient>
- <linearGradient
- id="linearGradient3366">
- <stop
- style="stop-color:#c3b08a;stop-opacity:1;"
- offset="0"
- id="stop3368" />
- <stop
- style="stop-color:#6a5c43;stop-opacity:1;"
- offset="1"
- id="stop3370" />
- </linearGradient>
- <linearGradient
- id="linearGradient4477">
- <stop
- id="stop4479"
- offset="0"
- style="stop-color:#99df59;stop-opacity:1;" />
- <stop
- id="stop4481"
- offset="1"
- style="stop-color:#1c830a;stop-opacity:1;" />
- </linearGradient>
- <linearGradient
- gradientTransform="translate(195.94286,16.830344)"
- y2="466.27859"
- x2="319.18427"
- y1="428.16779"
- x1="367.90091"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6132"
- xlink:href="#linearGradient5168"
- inkscape:collect="always" />
- <linearGradient
- id="linearGradient5168">
- <stop
- id="stop5170"
- offset="0"
- style="stop-color:#818181;stop-opacity:1;" />
- <stop
- id="stop5172"
- offset="1"
- style="stop-color:#e4e4e4;stop-opacity:1;" />
- </linearGradient>
- <linearGradient
- gradientTransform="translate(195.94286,16.830344)"
- y2="371.05057"
- x2="361.7919"
- y1="350.3981"
- x1="361.67862"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6129"
- xlink:href="#linearGradient5168"
- inkscape:collect="always" />
- <linearGradient
- gradientTransform="translate(195.94286,16.830344)"
- y2="490.77036"
- x2="372.45071"
- y1="425.21579"
- x1="393.01285"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6125"
- xlink:href="#linearGradient5168"
- inkscape:collect="always" />
- <linearGradient
- id="linearGradient6414">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop6416" />
- <stop
- style="stop-color:#89b9ff;stop-opacity:0;"
- offset="1"
- id="stop6418" />
- </linearGradient>
- <linearGradient
- id="linearGradient8128"
- x1="-292.10534"
- x2="-272.05444"
- xlink:href="#linearGradient24634"
- y1="801.15871"
- y2="767.89466"
- gradientTransform="scale(0.94125053,1.0624164)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8125"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0,0,0.696436,45.3144,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8123"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8120"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0,0,0.920262,45.3144,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8118"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8115"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0,0,0.840532,45.3144,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8101"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8098"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6125-4"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6123"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6121"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6119"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6117"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6115"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6113"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42621"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42619"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42617"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42615"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42613"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42611"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42609"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42539"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42537"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42535"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42533"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42531"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42455"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42453"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42451"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42449"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42447"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42371"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42369"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42367"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42365"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42329"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42327"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42325"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42323"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42261"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42233"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42231"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42229"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42227"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42225"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42149"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42147"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42145"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42143"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42141"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42139"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42137"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42081"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42079"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42077"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42075"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42073"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42071"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42069"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42067"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42065"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42063"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42061"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42059"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42057"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42055"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42053"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42051"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42049"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient42047"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.371181,0.000000,0.000000,0.729298,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42045"
- x1="-380.04468"
- x2="-370.10059"
- xlink:href="#linearGradient11829"
- y1="1007.2507"
- y2="1016.4421" />
- <linearGradient
- gradientTransform="matrix(1.429174,0.000000,0.000000,0.699705,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42043"
- x1="-349.88315"
- x2="-322.99942"
- xlink:href="#linearGradient11001"
- y1="1239.4677"
- y2="1260.8375" />
- <linearGradient
- gradientTransform="matrix(1.359357,0.000000,0.000000,0.735642,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42041"
- x1="-404.03406"
- x2="-380.20712"
- xlink:href="#linearGradient11011"
- y1="1081.1377"
- y2="1102.4374" />
- <linearGradient
- gradientTransform="matrix(0.811553,0.000000,0.000000,1.232206,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42039"
- x1="-798.23969"
- x2="-797.20386"
- xlink:href="#linearGradient16064"
- y1="651.72504"
- y2="642.56323" />
- <linearGradient
- gradientTransform="matrix(1.603962,0.000000,0.000000,0.623456,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42037"
- x1="-381.15289"
- x2="-381.04626"
- xlink:href="#linearGradient15247"
- y1="1295.5891"
- y2="1266.595" />
- <linearGradient
- gradientTransform="matrix(1.315908,0.000000,0.000000,0.759932,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42035"
- x1="-262.21292"
- x2="-262.53293"
- xlink:href="#linearGradient13619"
- y1="1062.0621"
- y2="1029.5856" />
- <linearGradient
- gradientTransform="matrix(0.785956,0.000000,0.000000,1.272336,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42033"
- x1="-594.90228"
- x2="-481.1633"
- xlink:href="#linearGradient8564"
- y1="621.28558"
- y2="684.27393" />
- <linearGradient
- gradientTransform="matrix(0.769044,0.000000,0.000000,1.300316,-11.99999,20.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42031"
- x1="-712.03973"
- x2="-664.81915"
- xlink:href="#linearGradient5296"
- y1="618.224"
- y2="689.1936" />
- <linearGradient
- gradientTransform="matrix(1.258563,0.000000,0.000000,0.794557,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42029"
- x1="-337.15262"
- x2="-357.19342"
- xlink:href="#linearGradient6930"
- y1="1111.5994"
- y2="1089.6976" />
- <linearGradient
- gradientTransform="matrix(1.292530,0.000000,0.000000,0.773676,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42027"
- x1="-400.31952"
- x2="-364.75821"
- xlink:href="#linearGradient6114"
- y1="961.79327"
- y2="997.6897" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42025"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42023"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42021"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42019"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42017"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42015"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42013"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42011"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42009"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42007"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42005"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42003"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient42001"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41999"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41997"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41995"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41993"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41991"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41989"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41987"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41985"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41983"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient41981"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41979"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41977"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41975"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41973"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41971"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41969"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41967"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41965"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient41963"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient41961"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient41959"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41957"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41955"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41953"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient41951"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41949"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41947"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41945"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41943"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41941"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41939"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41937"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41935"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41933"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41931"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41929"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41927"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41925"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41923"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41921"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41133"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41131"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41129"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41127"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41125"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41123"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41121"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41119"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41117"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41115"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41113"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41111"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41109"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41107"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41105"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41103"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41101"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41099"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41097"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41095"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41093"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41091"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41089"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41087"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41085"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41083"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41081"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41079"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41077"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41075"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41073"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41071"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41069"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41067"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41065"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41063"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41061"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41059"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41057"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41055"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41053"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41051"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41049"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41047"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41045"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41043"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41041"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41039"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41037"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41035"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41033"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41031"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41029"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41027"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41025"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41023"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41021"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41019"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41017"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41015"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41013"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41011"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41009"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41007"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41005"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41003"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient41001"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40999"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40997"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40995"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40993"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40991"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40989"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40987"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40985"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40983"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40981"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40979"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40977"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40975"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40973"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40971"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40969"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40967"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40965"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40963"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(1.371181,0.000000,0.000000,0.729298,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40961"
- x1="-380.04468"
- x2="-370.10059"
- xlink:href="#linearGradient11829"
- y1="1007.2507"
- y2="1016.4421" />
- <linearGradient
- gradientTransform="matrix(1.429174,0.000000,0.000000,0.699705,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40959"
- x1="-349.88315"
- x2="-322.99942"
- xlink:href="#linearGradient11001"
- y1="1239.4677"
- y2="1260.8375" />
- <linearGradient
- gradientTransform="matrix(1.359357,0.000000,0.000000,0.735642,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40957"
- x1="-404.03406"
- x2="-380.20712"
- xlink:href="#linearGradient11011"
- y1="1081.1377"
- y2="1102.4374" />
- <linearGradient
- gradientTransform="matrix(0.811553,0.000000,0.000000,1.232206,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40955"
- x1="-798.23969"
- x2="-797.20386"
- xlink:href="#linearGradient16064"
- y1="651.72504"
- y2="642.56323" />
- <linearGradient
- gradientTransform="matrix(1.603962,0.000000,0.000000,0.623456,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40953"
- x1="-381.15289"
- x2="-381.04626"
- xlink:href="#linearGradient15247"
- y1="1295.5891"
- y2="1266.595" />
- <linearGradient
- gradientTransform="matrix(1.315908,0.000000,0.000000,0.759932,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40951"
- x1="-262.21292"
- x2="-262.53293"
- xlink:href="#linearGradient13619"
- y1="1062.0621"
- y2="1029.5856" />
- <linearGradient
- gradientTransform="matrix(0.785956,0.000000,0.000000,1.272336,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40949"
- x1="-594.90228"
- x2="-481.1633"
- xlink:href="#linearGradient8564"
- y1="621.28558"
- y2="684.27393" />
- <linearGradient
- gradientTransform="matrix(0.769044,0.000000,0.000000,1.300316,-11.99999,20.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40947"
- x1="-712.03973"
- x2="-664.81915"
- xlink:href="#linearGradient5296"
- y1="618.224"
- y2="689.1936" />
- <linearGradient
- gradientTransform="matrix(1.258563,0.000000,0.000000,0.794557,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40945"
- x1="-337.15262"
- x2="-357.19342"
- xlink:href="#linearGradient6930"
- y1="1111.5994"
- y2="1089.6976" />
- <linearGradient
- gradientTransform="matrix(1.292530,0.000000,0.000000,0.773676,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40943"
- x1="-400.31952"
- x2="-364.75821"
- xlink:href="#linearGradient6114"
- y1="961.79327"
- y2="997.6897" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40941"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40939"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40937"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40935"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40933"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40931"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40457"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40455"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40453"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40451"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40389"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40387"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40385"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40383"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40321"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40319"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40317"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40315"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40253"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40251"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40249"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40247"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40223"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40221"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40219"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40189"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40187"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40185"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40183"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40181"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40179"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40177"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40175"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40173"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40171"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40169"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40167"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40165"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40163"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40161"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40159"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40157"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40155"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40153"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40151"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40149"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40147"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40145"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40143"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40141"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40139"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40137"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40135"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40133"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40131"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40129"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40127"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40125"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40123"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40121"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40119"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40117"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40115"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40113"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40111"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40109"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40107"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40105"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40103"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40101"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40099"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40097"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40095"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40093"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40091"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40089"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40087"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40085"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40083"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40081"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40079"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40077"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40075"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40073"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40071"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40069"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40067"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40065"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40063"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40061"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40059"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40057"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40055"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40053"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40051"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40049"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40047"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40045"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40043"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40041"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40039"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40037"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40035"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40033"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40031"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40029"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40027"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40025"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40023"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40021"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40019"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40017"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40015"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40013"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40011"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40009"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40007"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40005"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient40003"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient40001"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39999"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39997"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39995"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39993"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39991"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(1.371181,0.000000,0.000000,0.729298,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39989"
- x1="-380.04468"
- x2="-370.10059"
- xlink:href="#linearGradient11829"
- y1="1007.2507"
- y2="1016.4421" />
- <linearGradient
- gradientTransform="matrix(1.429174,0.000000,0.000000,0.699705,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39987"
- x1="-349.88315"
- x2="-322.99942"
- xlink:href="#linearGradient11001"
- y1="1239.4677"
- y2="1260.8375" />
- <linearGradient
- gradientTransform="matrix(1.359357,0.000000,0.000000,0.735642,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39985"
- x1="-404.03406"
- x2="-380.20712"
- xlink:href="#linearGradient11011"
- y1="1081.1377"
- y2="1102.4374" />
- <linearGradient
- gradientTransform="matrix(0.811553,0.000000,0.000000,1.232206,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39983"
- x1="-798.23969"
- x2="-797.20386"
- xlink:href="#linearGradient16064"
- y1="651.72504"
- y2="642.56323" />
- <linearGradient
- gradientTransform="matrix(1.603962,0.000000,0.000000,0.623456,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39981"
- x1="-381.15289"
- x2="-381.04626"
- xlink:href="#linearGradient15247"
- y1="1295.5891"
- y2="1266.595" />
- <linearGradient
- gradientTransform="matrix(1.315908,0.000000,0.000000,0.759932,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39979"
- x1="-262.21292"
- x2="-262.53293"
- xlink:href="#linearGradient13619"
- y1="1062.0621"
- y2="1029.5856" />
- <linearGradient
- gradientTransform="matrix(0.785956,0.000000,0.000000,1.272336,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39977"
- x1="-594.90228"
- x2="-481.1633"
- xlink:href="#linearGradient8564"
- y1="621.28558"
- y2="684.27393" />
- <linearGradient
- gradientTransform="matrix(0.769044,0.000000,0.000000,1.300316,-11.99999,20.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39975"
- x1="-712.03973"
- x2="-664.81915"
- xlink:href="#linearGradient5296"
- y1="618.224"
- y2="689.1936" />
- <linearGradient
- gradientTransform="matrix(1.258563,0.000000,0.000000,0.794557,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39973"
- x1="-337.15262"
- x2="-357.19342"
- xlink:href="#linearGradient6930"
- y1="1111.5994"
- y2="1089.6976" />
- <linearGradient
- gradientTransform="matrix(1.292530,0.000000,0.000000,0.773676,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39971"
- x1="-400.31952"
- x2="-364.75821"
- xlink:href="#linearGradient6114"
- y1="961.79327"
- y2="997.6897" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39969"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39967"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39965"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39963"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39961"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39959"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39957"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39955"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39953"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39951"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.371181,0.000000,0.000000,0.729298,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39949"
- x1="-380.04468"
- x2="-370.10059"
- xlink:href="#linearGradient11829"
- y1="1007.2507"
- y2="1016.4421" />
- <linearGradient
- gradientTransform="matrix(1.429174,0.000000,0.000000,0.699705,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39947"
- x1="-349.88315"
- x2="-322.99942"
- xlink:href="#linearGradient11001"
- y1="1239.4677"
- y2="1260.8375" />
- <linearGradient
- gradientTransform="matrix(1.359357,0.000000,0.000000,0.735642,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39945"
- x1="-404.03406"
- x2="-380.20712"
- xlink:href="#linearGradient11011"
- y1="1081.1377"
- y2="1102.4374" />
- <linearGradient
- gradientTransform="matrix(0.811553,0.000000,0.000000,1.232206,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39943"
- x1="-798.23969"
- x2="-797.20386"
- xlink:href="#linearGradient16064"
- y1="651.72504"
- y2="642.56323" />
- <linearGradient
- gradientTransform="matrix(1.603962,0.000000,0.000000,0.623456,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39941"
- x1="-381.15289"
- x2="-381.04626"
- xlink:href="#linearGradient15247"
- y1="1295.5891"
- y2="1266.595" />
- <linearGradient
- gradientTransform="matrix(1.315908,0.000000,0.000000,0.759932,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39939"
- x1="-262.21292"
- x2="-262.53293"
- xlink:href="#linearGradient13619"
- y1="1062.0621"
- y2="1029.5856" />
- <linearGradient
- gradientTransform="matrix(0.785956,0.000000,0.000000,1.272336,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39937"
- x1="-594.90228"
- x2="-481.1633"
- xlink:href="#linearGradient8564"
- y1="621.28558"
- y2="684.27393" />
- <linearGradient
- gradientTransform="matrix(0.769044,0.000000,0.000000,1.300316,-11.99999,20.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39935"
- x1="-712.03973"
- x2="-664.81915"
- xlink:href="#linearGradient5296"
- y1="618.224"
- y2="689.1936" />
- <linearGradient
- gradientTransform="matrix(1.258563,0.000000,0.000000,0.794557,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39933"
- x1="-337.15262"
- x2="-357.19342"
- xlink:href="#linearGradient6930"
- y1="1111.5994"
- y2="1089.6976" />
- <linearGradient
- gradientTransform="matrix(1.292530,0.000000,0.000000,0.773676,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39931"
- x1="-400.31952"
- x2="-364.75821"
- xlink:href="#linearGradient6114"
- y1="961.79327"
- y2="997.6897" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39929"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39927"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39925"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39923"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39921"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39919"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39917"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39915"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39913"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39911"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39909"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39907"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39905"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39903"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39901"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39899"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39897"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39895"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39893"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39891"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39889"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39887"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39885"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39883"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39881"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39879"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39877"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39875"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39873"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39871"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39869"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39867"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39865"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient39863"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39861"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39859"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39857"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39855"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39853"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39851"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient39849"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36636"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36634"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36632"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36630"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36628"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36626"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36624"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36548"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36546"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36544"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36542"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36480"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36478"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36476"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient36446"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36444"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36442"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36440"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36416"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient36414"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient36412"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient36410"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36374"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36372"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36370"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36368"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36366"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36364"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36362"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36298"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36296"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36294"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36292"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36290"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36288"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36286"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36194"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36192"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36190"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36188"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36186"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36184"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36182"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36118"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.371181,0.000000,0.000000,0.729298,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36060"
- x1="-380.04468"
- x2="-370.10059"
- xlink:href="#linearGradient11829"
- y1="1007.2507"
- y2="1016.4421" />
- <linearGradient
- gradientTransform="matrix(1.429174,0.000000,0.000000,0.699705,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36058"
- x1="-349.88315"
- x2="-322.99942"
- xlink:href="#linearGradient11001"
- y1="1239.4677"
- y2="1260.8375" />
- <linearGradient
- gradientTransform="matrix(1.359357,0.000000,0.000000,0.735642,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36056"
- x1="-404.03406"
- x2="-380.20712"
- xlink:href="#linearGradient11011"
- y1="1081.1377"
- y2="1102.4374" />
- <linearGradient
- gradientTransform="matrix(0.811553,0.000000,0.000000,1.232206,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36054"
- x1="-798.23969"
- x2="-797.20386"
- xlink:href="#linearGradient16064"
- y1="651.72504"
- y2="642.56323" />
- <linearGradient
- gradientTransform="matrix(1.603962,0.000000,0.000000,0.623456,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36052"
- x1="-381.15289"
- x2="-381.04626"
- xlink:href="#linearGradient15247"
- y1="1295.5891"
- y2="1266.595" />
- <linearGradient
- gradientTransform="matrix(1.315908,0.000000,0.000000,0.759932,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36050"
- x1="-262.21292"
- x2="-262.53293"
- xlink:href="#linearGradient13619"
- y1="1062.0621"
- y2="1029.5856" />
- <linearGradient
- gradientTransform="matrix(0.785956,0.000000,0.000000,1.272336,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36048"
- x1="-594.90228"
- x2="-481.1633"
- xlink:href="#linearGradient8564"
- y1="621.28558"
- y2="684.27393" />
- <linearGradient
- gradientTransform="matrix(0.769044,0.000000,0.000000,1.300316,-11.99999,20.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36046"
- x1="-712.03973"
- x2="-664.81915"
- xlink:href="#linearGradient5296"
- y1="618.224"
- y2="689.1936" />
- <linearGradient
- gradientTransform="matrix(1.258563,0.000000,0.000000,0.794557,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36044"
- x1="-337.15262"
- x2="-357.19342"
- xlink:href="#linearGradient6930"
- y1="1111.5994"
- y2="1089.6976" />
- <linearGradient
- gradientTransform="matrix(1.292530,0.000000,0.000000,0.773676,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36042"
- x1="-400.31952"
- x2="-364.75821"
- xlink:href="#linearGradient6114"
- y1="961.79327"
- y2="997.6897" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35994"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35992"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35990"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35988"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35952"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35950"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35948"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35946"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35944"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35942"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35940"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35938"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35936"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35934"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35932"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35930"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35928"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35926"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35924"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35922"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35920"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35918"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35916"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35914"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35912"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35910"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35908"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35906"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35904"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35902"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35900"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35898"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35896"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35894"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35892"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35890"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35888"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35886"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35884"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35882"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35880"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35878"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35876"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35874"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35872"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35870"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35868"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35866"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35864"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35862"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35860"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35858"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35856"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35854"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35852"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35850"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35848"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35846"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35844"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35842"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35840"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35838"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35836"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35834"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35832"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35830"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35828"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35826"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35824"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35822"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35820"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35818"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35816"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35814"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35812"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35810"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35808"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35806"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35804"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35802"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35800"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35798"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35796"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35794"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35792"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35790"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35788"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35786"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35784"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35782"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(1.371181,0.000000,0.000000,0.729298,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35780"
- x1="-380.04468"
- x2="-370.10059"
- xlink:href="#linearGradient11829"
- y1="1007.2507"
- y2="1016.4421" />
- <linearGradient
- gradientTransform="matrix(1.429174,0.000000,0.000000,0.699705,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35778"
- x1="-349.88315"
- x2="-322.99942"
- xlink:href="#linearGradient11001"
- y1="1239.4677"
- y2="1260.8375" />
- <linearGradient
- gradientTransform="matrix(1.359357,0.000000,0.000000,0.735642,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35776"
- x1="-404.03406"
- x2="-380.20712"
- xlink:href="#linearGradient11011"
- y1="1081.1377"
- y2="1102.4374" />
- <linearGradient
- gradientTransform="matrix(0.811553,0.000000,0.000000,1.232206,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35774"
- x1="-798.23969"
- x2="-797.20386"
- xlink:href="#linearGradient16064"
- y1="651.72504"
- y2="642.56323" />
- <linearGradient
- gradientTransform="matrix(1.603962,0.000000,0.000000,0.623456,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35772"
- x1="-381.15289"
- x2="-381.04626"
- xlink:href="#linearGradient15247"
- y1="1295.5891"
- y2="1266.595" />
- <linearGradient
- gradientTransform="matrix(1.315908,0.000000,0.000000,0.759932,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35770"
- x1="-262.21292"
- x2="-262.53293"
- xlink:href="#linearGradient13619"
- y1="1062.0621"
- y2="1029.5856" />
- <linearGradient
- gradientTransform="matrix(0.785956,0.000000,0.000000,1.272336,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35768"
- x1="-594.90228"
- x2="-481.1633"
- xlink:href="#linearGradient8564"
- y1="621.28558"
- y2="684.27393" />
- <linearGradient
- gradientTransform="matrix(0.769044,0.000000,0.000000,1.300316,-11.99999,20.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35766"
- x1="-712.03973"
- x2="-664.81915"
- xlink:href="#linearGradient5296"
- y1="618.224"
- y2="689.1936" />
- <linearGradient
- gradientTransform="matrix(1.258563,0.000000,0.000000,0.794557,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35764"
- x1="-337.15262"
- x2="-357.19342"
- xlink:href="#linearGradient6930"
- y1="1111.5994"
- y2="1089.6976" />
- <linearGradient
- gradientTransform="matrix(1.292530,0.000000,0.000000,0.773676,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35762"
- x1="-400.31952"
- x2="-364.75821"
- xlink:href="#linearGradient6114"
- y1="961.79327"
- y2="997.6897" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35760"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35758"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35756"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35754"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient35752"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35750"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35290"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35288"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35286"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35284"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35006"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35004"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35002"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35000"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient34998"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient34996"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient34994"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="scale(1.131681,0.883641)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32079"
- x1="18.556467"
- x2="276.57971"
- xlink:href="#linearGradient32063"
- y1="154.01553"
- y2="154.01553" />
- <linearGradient
- gradientTransform="scale(1.231824,0.811804)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32077"
- x1="302.80289"
- x2="586.1225"
- xlink:href="#linearGradient32063"
- y1="150.39896"
- y2="150.39896" />
- <linearGradient
- gradientTransform="scale(1.163014,0.859835)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32075"
- x1="692.16718"
- x2="893.36859"
- xlink:href="#linearGradient32063"
- y1="168.16537"
- y2="168.16537" />
- <linearGradient
- gradientTransform="scale(1.249097,0.800579)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32073"
- x1="646.06696"
- x2="830.20001"
- xlink:href="#linearGradient32063"
- y1="395.19913"
- y2="395.19913" />
- <linearGradient
- gradientTransform="scale(1.336686,0.748119)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32071"
- x1="490.7659"
- x2="772.8067"
- xlink:href="#linearGradient32063"
- y1="822.85663"
- y2="822.85663" />
- <linearGradient
- gradientTransform="scale(1.080392,0.925590)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32069"
- x1="21.288565"
- x2="331.36115"
- xlink:href="#linearGradient32063"
- y1="634.83252"
- y2="634.83252" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient27043"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient27041"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26939"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26937"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26935"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26933"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26931"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26929"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26927"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26925"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26923"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26921"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26919"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26917"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26915"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26913"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26911"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26909"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26907"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26905"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26903"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26901"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26899"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26897"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26895"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26893"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26891"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26889"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26887"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26885"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26883"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26881"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26879"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26877"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26875"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26655"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26653"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26651"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26649"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26647"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26645"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26643"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26641"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26639"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26637"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26635"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26633"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26631"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26629"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26627"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26625"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26623"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26621"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26619"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26617"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26615"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26613"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26611"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26609"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26607"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26605"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26603"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26601"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26599"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26597"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26595"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26593"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26591"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26371"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26369"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26367"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26365"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26363"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26361"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26359"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26357"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26355"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26353"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26351"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26349"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26347"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26345"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26343"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26341"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26339"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26337"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26335"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26333"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26331"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26329"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26327"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26325"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26323"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26321"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26319"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26317"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26315"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26313"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26311"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26309"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26307"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26081"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26079"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26077"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26047"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.371181,0.000000,0.000000,0.729298,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26019"
- x1="-380.04468"
- x2="-370.10059"
- xlink:href="#linearGradient11829"
- y1="1007.2507"
- y2="1016.4421" />
- <linearGradient
- gradientTransform="matrix(1.429174,0.000000,0.000000,0.699705,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26017"
- x1="-349.88315"
- x2="-322.99942"
- xlink:href="#linearGradient11001"
- y1="1239.4677"
- y2="1260.8375" />
- <linearGradient
- gradientTransform="matrix(1.359357,0.000000,0.000000,0.735642,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26015"
- x1="-404.03406"
- x2="-380.20712"
- xlink:href="#linearGradient11011"
- y1="1081.1377"
- y2="1102.4374" />
- <linearGradient
- gradientTransform="matrix(0.811553,0.000000,0.000000,1.232206,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26013"
- x1="-798.23969"
- x2="-797.20386"
- xlink:href="#linearGradient16064"
- y1="651.72504"
- y2="642.56323" />
- <linearGradient
- gradientTransform="matrix(1.603962,0.000000,0.000000,0.623456,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26011"
- x1="-381.15289"
- x2="-381.04626"
- xlink:href="#linearGradient15247"
- y1="1295.5891"
- y2="1266.595" />
- <linearGradient
- gradientTransform="matrix(1.315908,0.000000,0.000000,0.759932,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26009"
- x1="-262.21292"
- x2="-262.53293"
- xlink:href="#linearGradient13619"
- y1="1062.0621"
- y2="1029.5856" />
- <linearGradient
- gradientTransform="matrix(0.785956,0.000000,0.000000,1.272336,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26007"
- x1="-594.90228"
- x2="-481.1633"
- xlink:href="#linearGradient8564"
- y1="621.28558"
- y2="684.27393" />
- <linearGradient
- gradientTransform="matrix(0.769044,0.000000,0.000000,1.300316,-11.99999,20.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26005"
- x1="-712.03973"
- x2="-664.81915"
- xlink:href="#linearGradient5296"
- y1="618.224"
- y2="689.1936" />
- <linearGradient
- gradientTransform="matrix(1.258563,0.000000,0.000000,0.794557,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26003"
- x1="-337.15262"
- x2="-357.19342"
- xlink:href="#linearGradient6930"
- y1="1111.5994"
- y2="1089.6976" />
- <linearGradient
- gradientTransform="matrix(1.292530,0.000000,0.000000,0.773676,-13.00000,19.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient26001"
- x1="-400.31952"
- x2="-364.75821"
- xlink:href="#linearGradient6114"
- y1="961.79327"
- y2="997.6897" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25953"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25909"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25865"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient25863"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient25861"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient25859"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25823"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient25821"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient25819"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient25817"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25781"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25779"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25777"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25775"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25773"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25771"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25769"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25713"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25669"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25639"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25637"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25635"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25633"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25631"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25629"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25627"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25459"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25457"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25455"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25453"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25451"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25449"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25447"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(2.264569,0.000000,0.000000,0.441585,173.0000,167.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25313"
- x1="-165.71484"
- x2="-181.03664"
- xlink:href="#linearGradient20407"
- y1="1612.2013"
- y2="1656.2878" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25269"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25239"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25237"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25235"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25233"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25231"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25229"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25227"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25225"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25223"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25221"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25219"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25217"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25215"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25213"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25211"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25209"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25207"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25205"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25203"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25201"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25199"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25197"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25195"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25193"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25191"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25189"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25187"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25185"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25183"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25181"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25179"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25177"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-727.1488,74.13503)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient25175"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24778"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24776"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24774"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24772"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24770"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient24768"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient24766"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="matrix(0.942983,0.000000,0.000000,1.060465,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24670"
- x1="-239.39154"
- x2="-219.37743"
- xlink:href="#linearGradient24634"
- y1="819.75134"
- y2="786.42609" />
- <radialGradient
- cx="-196.64432"
- cy="1148.3601"
- fx="-196.99765"
- fy="1149.8182"
- gradientTransform="matrix(1.435882,0.000000,0.000000,0.696436,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient24668"
- r="43.91539"
- xlink:href="#linearGradient23816" />
- <radialGradient
- cx="-270.41168"
- cy="944.0636"
- fx="-268.89566"
- fy="944.96375"
- gradientTransform="matrix(1.086647,0.000000,0.000000,0.920262,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient24666"
- r="60.125954"
- xlink:href="#linearGradient22998" />
- <radialGradient
- cx="-275.86343"
- cy="1062.9248"
- fx="-278.02069"
- fy="1062.9252"
- gradientTransform="matrix(1.189722,0.000000,0.000000,0.840532,45.31440,33.32412)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient24664"
- r="195.22539"
- xlink:href="#linearGradient22180" />
- <linearGradient
- id="linearGradient20478"
- x1="-89.320584"
- x2="-104.64242"
- xlink:href="#linearGradient20407"
- y1="1990.3841"
- y2="2034.4706"
- gradientTransform="scale(2.2645694,0.44158505)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16156"
- x1="-443.96327"
- x2="-434.01914"
- xlink:href="#linearGradient11829"
- y1="962.02058"
- y2="971.21196"
- gradientTransform="scale(1.3711807,0.72929848)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16154"
- x1="-411.20798"
- x2="-384.32428"
- xlink:href="#linearGradient11001"
- y1="1192.3256"
- y2="1213.6953"
- gradientTransform="scale(1.4291738,0.69970497)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16152"
- x1="-468.50815"
- x2="-444.68124"
- xlink:href="#linearGradient11011"
- y1="1036.2992"
- y2="1057.599"
- gradientTransform="scale(1.359358,0.73564138)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16150"
- x1="-906.23625"
- x2="-905.20042"
- xlink:href="#linearGradient16064"
- y1="624.95469"
- y2="615.79288"
- gradientTransform="scale(0.8115517,1.2322074)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16148"
- x1="-449.94693"
- x2="-449.83682"
- xlink:href="#linearGradient15247"
- y1="1203.5956"
- y2="1175.5135"
- gradientTransform="scale(1.5535134,0.64370221)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16146"
- x1="-328.9941"
- x2="-329.31392"
- xlink:href="#linearGradient13619"
- y1="1018.0411"
- y2="985.58416"
- gradientTransform="scale(1.3151129,0.76039098)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16144"
- x1="-706.41478"
- x2="-592.67569"
- xlink:href="#linearGradient8564"
- y1="594.88415"
- y2="658.30523"
- gradientTransform="scale(0.78595584,1.2723361)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16142"
- x1="-824.70418"
- x2="-777.48365"
- xlink:href="#linearGradient5296"
- y1="593.62552"
- y2="664.5951"
- gradientTransform="scale(0.76904381,1.300316)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16140"
- x1="-406.79069"
- x2="-426.83155"
- xlink:href="#linearGradient6930"
- y1="1070.0845"
- y2="1048.1827"
- gradientTransform="scale(1.2585627,0.79455718)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient16138"
- x1="-471.05217"
- x2="-434.37563"
- xlink:href="#linearGradient6114"
- y1="919.15765"
- y2="955.05408"
- gradientTransform="scale(1.29253,0.77367642)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11539"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient9432"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9430"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9428"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9426"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9402"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9400"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9398"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9396"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9394"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient9318"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9316"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9314"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9312"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9310"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9308"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9306"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9304"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9302"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9300"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9298"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9186"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9184"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9182"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9180"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9178"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9176"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9174"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient9172"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9170"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9168"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9166"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9164"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9162"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9160"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9158"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9156"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9154"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9152"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9150"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9148"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9146"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9144"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9142"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9140"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9138"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9136"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient9134"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient9132"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9130"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9128"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9126"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient9124"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9122"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9120"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9118"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9116"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9114"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9112"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9110"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9108"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9106"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient9104"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9102"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9100"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9098"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8656"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8654"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8652"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8642"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8640"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8638"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8636"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8634"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8632"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8630"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8438"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8436"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8434"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8432"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <radialGradient
- cx="-859.63184"
- cy="411.53275"
- fx="-859.63184"
- fy="411.53275"
- gradientTransform="matrix(0.899524,0.000000,0.000000,1.111699,-3.573880,-0.999998)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient8408"
- r="4.241514"
- xlink:href="#linearGradient8380" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8406"
- x1="-568.83093"
- x2="-505.96338"
- xlink:href="#linearGradient7562"
- y1="647.1355"
- y2="647.1355" />
- <linearGradient
- gradientTransform="matrix(1.667805,0.000000,0.000000,0.599590,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8404"
- x1="-467.68597"
- x2="-400.48749"
- xlink:href="#linearGradient7562"
- y1="734.48993"
- y2="734.48993" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,0.935124,-0.936978)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8402"
- x1="-767.16602"
- x2="-723.23309"
- xlink:href="#linearGradient7562"
- y1="421.63675"
- y2="421.63675" />
- <linearGradient
- gradientTransform="matrix(1.147664,0.000000,0.000000,0.871335,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5635"
- x1="707.46619"
- x2="734.53558"
- xlink:href="#linearGradient15239"
- y1="437.9357"
- y2="437.9357" />
- <linearGradient
- gradientTransform="matrix(1.594210,0.000000,0.000000,0.627270,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5633"
- x1="517.76581"
- x2="543.48871"
- xlink:href="#linearGradient15239"
- y1="433.19632"
- y2="433.19632" />
- <linearGradient
- gradientTransform="matrix(1.102149,0.000000,0.000000,0.907318,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5631"
- x1="769.37439"
- x2="746.28076"
- xlink:href="#linearGradient14435"
- y1="333.58838"
- y2="312.47925" />
- <linearGradient
- gradientTransform="matrix(0.819029,0.000000,0.000000,1.220958,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5629"
- x1="1186.8965"
- x2="1138.142"
- xlink:href="#linearGradient14425"
- y1="249.12363"
- y2="230.35275" />
- <linearGradient
- gradientTransform="matrix(0.981927,0.000000,0.000000,1.018405,-110.0000,126.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5627"
- x1="944.16394"
- x2="918.84033"
- xlink:href="#linearGradient14415"
- y1="403.95618"
- y2="345.91528" />
- <linearGradient
- gradientTransform="matrix(0.324477,0.000000,0.000000,0.572752,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5625"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(0.296117,0.000000,0.000000,0.627606,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5623"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="matrix(0.385404,0.000000,0.000000,0.482208,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5621"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="matrix(0.473718,0.000000,0.000000,0.392312,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5619"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="matrix(0.493766,0.000000,0.000000,0.376383,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5617"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="matrix(0.469408,0.000000,0.000000,0.395913,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5615"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="matrix(0.466498,0.000000,0.000000,0.398382,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5613"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="matrix(0.429102,0.000000,0.000000,0.433103,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5611"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="matrix(0.491190,0.000000,0.000000,0.378357,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5609"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="matrix(0.370291,0.000000,0.000000,0.501888,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5607"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="matrix(0.411417,0.000000,0.000000,0.451719,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5605"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="matrix(0.390690,0.000000,0.000000,0.475683,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5603"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="matrix(0.415250,0.000000,0.000000,0.447549,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5601"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="matrix(0.472469,0.000000,0.000000,0.393348,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5599"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="matrix(0.441566,0.000000,0.000000,0.420877,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5597"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="matrix(0.539646,0.000000,0.000000,0.344383,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5595"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="matrix(0.515296,0.000000,0.000000,0.360656,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5593"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="matrix(0.462732,0.000000,0.000000,0.401625,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5591"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="matrix(0.517828,0.000000,0.000000,0.358893,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5589"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="matrix(0.527045,0.000000,0.000000,0.352616,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5587"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="matrix(0.456132,0.000000,0.000000,0.407437,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5585"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="matrix(0.380542,0.000000,0.000000,0.488370,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5583"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="matrix(0.427164,0.000000,0.000000,0.435066,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5581"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="matrix(0.424124,0.000000,0.000000,0.438185,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5579"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="matrix(0.471220,0.000000,0.000000,0.394392,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5577"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="matrix(0.421316,0.000000,0.000000,0.441106,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5575"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="matrix(0.433278,0.000000,0.000000,0.428927,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5573"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="matrix(0.396536,0.000000,0.000000,0.468671,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5571"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="matrix(0.580249,0.000000,0.000000,0.320284,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5569"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="matrix(0.663754,0.000000,0.000000,0.279991,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5567"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="matrix(0.436515,0.000000,0.000000,0.425746,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5565"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="matrix(0.436474,0.000000,0.000000,0.425787,-772.1814,181.3301)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5563"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="matrix(0.654620,0.000000,0.000000,0.506304,232.7412,101.3178)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5561"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5559"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5557"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5555"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5553"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5551"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5549"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5547"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5545"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5543"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5541"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5539"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5537"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5535"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5533"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5531"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5529"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5527"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5525"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5523"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5521"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5519"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5517"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5515"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5513"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5511"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5509"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5507"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5505"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5503"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5501"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5499"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5497"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="matrix(1.905755,0.000000,0.000000,1.693411,-433.6811,-89.81791)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5495"
- x1="546.26843"
- x2="545.60425"
- xlink:href="#linearGradient2408"
- y1="67.731834"
- y2="168.49771" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient5493"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5491"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5489"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5487"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5485"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5483"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5481"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5479"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5477"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient5475"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient5473"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="matrix(0.469226,0.000000,0.000000,0.809469,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5099"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="scale(1.195244,0.836650)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5097"
- x1="-505.37595"
- x2="-468.78281"
- xlink:href="#linearGradient7274"
- y1="366.48297"
- y2="402.41455" />
- <linearGradient
- gradientTransform="matrix(0.736627,0.000000,0.000000,0.515626,-890.8305,214.7769)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5095"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.735881,0.000000,0.000000,0.516149,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5093"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.592936,0.000000,0.000000,0.640582,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient5091"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.784596,0.000000,0.000000,0.484101,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient5089"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.724800,0.000000,0.000000,0.524039,-889.2404,215.6898)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient5087"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24980"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24978"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24976"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24974"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24972"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24970"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24968"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24946"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24944"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24942"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24940"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24938"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24936"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient24928"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24926"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24924"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24922"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24912"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24910"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24908"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24906"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24904"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24902"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient24900"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <linearGradient
- gradientTransform="matrix(1.155498,0.000000,0.000000,0.865428,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient21405"
- x1="436.86569"
- x2="450.00427"
- xlink:href="#linearGradient12202"
- y1="450.14758"
- y2="403.87964" />
- <linearGradient
- gradientTransform="matrix(1.142634,0.000000,0.000000,0.875171,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient21403"
- x1="452.32669"
- x2="469.11017"
- xlink:href="#linearGradient11434"
- y1="415.96478"
- y2="442.27634" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient21401"
- x1="363.52328"
- x2="331.53397"
- xlink:href="#linearGradient7622"
- y1="826.72278"
- y2="789.38806" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient21399"
- x1="242.22449"
- x2="304.7373"
- xlink:href="#linearGradient7622"
- y1="935.27197"
- y2="901.92621" />
- <linearGradient
- gradientTransform="matrix(1.685621,0.000000,0.000000,0.593253,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient21397"
- x1="241.83482"
- x2="306.9841"
- xlink:href="#linearGradient7622"
- y1="803.5163"
- y2="804.63666" />
- <linearGradient
- gradientTransform="matrix(0.839150,0.000000,0.000000,1.191683,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient21395"
- x1="616.49316"
- x2="775.58191"
- xlink:href="#linearGradient3776"
- y1="422.98691"
- y2="422.98706" />
- <linearGradient
- gradientTransform="matrix(0.899130,0.000000,0.000000,1.112186,-24.00000,21.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient21393"
- x1="586.46118"
- x2="406.9556"
- xlink:href="#linearGradient3006"
- y1="450.29825"
- y2="452.66983" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient20264"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20262"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20260"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20258"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0.000000,0.000000,1.184507,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient20218"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- gradientTransform="matrix(1.932073,0.000000,0.000000,0.517579,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20216"
- x1="439.81955"
- x2="442.64511"
- xlink:href="#linearGradient5072"
- y1="108.51342"
- y2="159.33141" />
- <linearGradient
- gradientTransform="matrix(0.851927,0.000000,0.000000,1.173809,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20214"
- x1="1018.5626"
- x2="969.65094"
- xlink:href="#linearGradient4304"
- y1="68.808693"
- y2="145.25305" />
- <linearGradient
- gradientTransform="matrix(0.693908,0.000000,0.000000,1.441113,-113.0000,-96.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20212"
- x1="1351.563"
- x2="1268.7037"
- xlink:href="#linearGradient3536"
- y1="111.27538"
- y2="100.71355" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20164"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20162"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20160"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20116"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient16059"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20114"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient15293"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-64.00000,111.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20112"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient15285"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20068"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20066"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20064"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20062"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20060"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient20058"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.526888,0.000000,0.000000,0.908943,183.5760,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19966"
- x1="577.008"
- x2="549.93549"
- xlink:href="#linearGradient8812"
- y1="71.327644"
- y2="98.11821" />
- <linearGradient
- gradientTransform="matrix(0.827149,0.000000,0.000000,0.578990,183.5760,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19964"
- x1="413.12741"
- x2="446.53641"
- xlink:href="#linearGradient7274"
- y1="204.25475"
- y2="234.64714" />
- <linearGradient
- gradientTransform="matrix(0.827149,0.000000,0.000000,0.578990,181.7905,166.2104)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19962"
- x1="249.4538"
- x2="345.66855"
- xlink:href="#linearGradient7274"
- y1="98.588005"
- y2="166.91022" />
- <linearGradient
- gradientTransform="matrix(0.826312,0.000000,0.000000,0.579577,183.5760,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19960"
- x1="362.67181"
- x2="384.60577"
- xlink:href="#linearGradient6500"
- y1="179.57222"
- y2="172.05354" />
- <linearGradient
- gradientTransform="matrix(0.665800,0.000000,0.000000,0.719302,183.5760,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19958"
- x1="456.70795"
- x2="512.44427"
- xlink:href="#linearGradient5730"
- y1="137.72455"
- y2="98.53508" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.881013,0.000000,0.000000,0.543591,183.5760,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient19956"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.813869,0.000000,0.000000,0.588437,183.5760,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient19954"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- gradientTransform="matrix(0.299637,0.000000,0.000000,0.511898,64.43069,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19900"
- x1="172.46669"
- x2="221.20189"
- xlink:href="#linearGradient3217"
- y1="243.81036"
- y2="236.42226" />
- <linearGradient
- gradientTransform="matrix(0.343249,0.000000,0.000000,0.446857,63.82445,512.4812)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19898"
- x1="174.09033"
- x2="186.00955"
- xlink:href="#linearGradient2431"
- y1="210.68542"
- y2="199.73466" />
- <linearGradient
- gradientTransform="matrix(0.349569,0.000000,0.000000,0.438779,63.18756,512.0919)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19896"
- x1="205.30519"
- x2="147.47412"
- xlink:href="#linearGradient2416"
- y1="196.64374"
- y2="227.68004" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19866"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19864"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19862"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient19860"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- id="linearGradient18891"
- x1="494.18576"
- x2="500.9045"
- xlink:href="#linearGradient9523"
- y1="667.3631"
- y2="664.3968"
- gradientTransform="scale(1.3013195,0.76845082)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18889"
- x1="579.94763"
- x2="600.35855"
- xlink:href="#linearGradient14218"
- y1="810.61072"
- y2="810.61072"
- gradientTransform="scale(1.24537,0.80297423)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18887"
- x1="631.82953"
- x2="649.22561"
- xlink:href="#linearGradient14218"
- y1="699.24799"
- y2="699.24799"
- gradientTransform="scale(1.0873188,0.91969348)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18885"
- x1="794.00612"
- x2="760.03882"
- xlink:href="#linearGradient11878"
- y1="549.17259"
- y2="656.59737"
- gradientTransform="scale(0.89198975,1.1210891)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18883"
- x1="901.41894"
- x2="929.23501"
- xlink:href="#linearGradient11110"
- y1="590.00247"
- y2="632.62064"
- gradientTransform="scale(0.89840854,1.1130794)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18881"
- x1="452.38991"
- x2="380.72752"
- xlink:href="#linearGradient10342"
- y1="1092.1932"
- y2="1016.5102"
- gradientTransform="scale(1.7510873,0.57107375)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18879"
- x1="607.2414"
- x2="702.19007"
- xlink:href="#linearGradient9573"
- y1="765.03152"
- y2="765.03152"
- gradientTransform="scale(1.2288373,0.81377736)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18486"
- x1="611.61928"
- x2="640.13083"
- xlink:href="#linearGradient15239"
- y1="582.5413"
- y2="582.5413"
- gradientTransform="scale(1.147664,0.87133513)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18484"
- x1="448.76625"
- x2="474.48916"
- xlink:href="#linearGradient15239"
- y1="634.06663"
- y2="634.06663"
- gradientTransform="scale(1.5942096,0.6272701)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18482"
- x1="669.5693"
- x2="646.47566"
- xlink:href="#linearGradient14435"
- y1="472.4591"
- y2="451.34997"
- gradientTransform="scale(1.1021491,0.90731822)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18480"
- x1="1052.5916"
- x2="1003.837"
- xlink:href="#linearGradient14425"
- y1="352.32113"
- y2="333.55029"
- gradientTransform="scale(0.8190287,1.2209584)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient18478"
- x1="832.1391"
- x2="806.81551"
- xlink:href="#linearGradient14415"
- y1="527.67886"
- y2="469.63794"
- gradientTransform="scale(0.98192722,1.0184054)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient10558"
- x1="572.53625"
- x2="571.34322"
- xlink:href="#linearGradient2408"
- y1="26.393758"
- y2="207.41442"
- gradientTransform="scale(1.0608458,0.94264409)"
- gradientUnits="userSpaceOnUse" />
- <radialGradient
- cx="977.47119"
- cy="130.81157"
- fx="978.28406"
- fy="131.98013"
- gradientTransform="matrix(0.844233,0,0,1.184507,-113,-96)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient10545"
- r="45.528042"
- xlink:href="#linearGradient7375" />
- <linearGradient
- id="linearGradient10543"
- x1="381.41198"
- x2="384.24586"
- xlink:href="#linearGradient5072"
- y1="-76.965554"
- y2="-26.147562"
- gradientTransform="scale(1.9320739,0.51757855)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient10541"
- x1="886.6912"
- x2="837.28759"
- xlink:href="#linearGradient4304"
- y1="-12.976363"
- y2="63.468046"
- gradientTransform="scale(0.85192731,1.1738091)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient10539"
- x1="1188.7164"
- x2="1105.8572"
- xlink:href="#linearGradient3536"
- y1="44.213111"
- y2="33.491605"
- gradientTransform="scale(0.6939085,1.4411122)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- gradientTransform="matrix(1.228837,0.000000,0.000000,0.813777,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient15034"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(1.751087,0.000000,0.000000,0.571074,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient15032"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="matrix(0.898409,0.000000,0.000000,1.113079,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient15030"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="matrix(0.891990,0.000000,0.000000,1.121089,87.00000,73.00000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient15028"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient15026"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,171.3691,-77.87950)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient15024"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient15022"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="matrix(1.300242,0.000000,0.000000,0.838354,84.36912,-150.8795)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient14226"
- x1="423.6752"
- x2="443.22476"
- xlink:href="#linearGradient14218"
- y1="869.29742"
- y2="869.29742" />
- <linearGradient
- gradientTransform="matrix(1.135228,0.000000,0.000000,0.960216,84.36912,-150.8795)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient14224"
- x1="454.2092"
- x2="470.87112"
- xlink:href="#linearGradient14218"
- y1="750.84491"
- y2="750.84491" />
- <linearGradient
- gradientTransform="scale(0.891990,1.121089)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11884"
- x1="696.47119"
- x2="662.50397"
- xlink:href="#linearGradient11878"
- y1="484.05737"
- y2="591.48218" />
- <linearGradient
- gradientTransform="scale(0.898409,1.113079)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11116"
- x1="804.58063"
- x2="832.39667"
- xlink:href="#linearGradient11110"
- y1="524.41888"
- y2="567.03705" />
- <linearGradient
- gradientTransform="scale(1.751087,0.571074)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient10348"
- x1="402.7066"
- x2="331.04416"
- xlink:href="#linearGradient10342"
- y1="964.36353"
- y2="888.68054" />
- <linearGradient
- gradientTransform="scale(1.228837,0.813777)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9579"
- x1="536.44293"
- x2="631.3916"
- xlink:href="#linearGradient9573"
- y1="675.32672"
- y2="675.32672" />
- <linearGradient
- gradientTransform="matrix(0.468119,0.000000,0.000000,0.276432,370.6729,434.1135)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient8000"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- id="linearGradient7973"
- x1="-363.76793"
- x2="-466.87414"
- xlink:href="#linearGradient7213"
- y1="-102.07738"
- y2="-101.26963"
- gradientTransform="scale(0.75267665,1.3285918)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7970"
- x1="-424.74813"
- x2="-330.8642"
- xlink:href="#linearGradient7981"
- y1="-92.793332"
- y2="-92.793332"
- gradientTransform="scale(0.68689099,1.4558351)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7966"
- x1="539.71451"
- x2="550.46717"
- xlink:href="#linearGradient9523"
- y1="279.15509"
- y2="274.40783"
- gradientTransform="scale(1.1370746,0.8794498)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7933"
- x1="-373.60034"
- x2="-360.91113"
- xlink:href="#linearGradient11227"
- y1="-159.87119"
- y2="-159.87119"
- gradientTransform="scale(0.8940075,1.1185589)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7930"
- x1="-306.22417"
- x2="-294.00085"
- xlink:href="#linearGradient11227"
- y1="-179.18895"
- y2="-179.18895"
- gradientTransform="scale(1.0988654,0.91002954)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7927"
- x1="-293.86906"
- x2="-283.07005"
- xlink:href="#linearGradient11227"
- y1="-172.43379"
- y2="-172.43379"
- gradientTransform="scale(1.1453712,0.8730794)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7924"
- x1="-307.61731"
- x2="-296.46066"
- xlink:href="#linearGradient11227"
- y1="-149.86558"
- y2="-149.86558"
- gradientTransform="scale(1.0888676,0.91838526)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7921"
- x1="-311.05315"
- x2="-297.7664"
- xlink:href="#linearGradient11227"
- y1="-135.2467"
- y2="-135.2467"
- gradientTransform="scale(1.0821187,0.924113)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7918"
- x1="-338.29211"
- x2="-327.62476"
- xlink:href="#linearGradient11227"
- y1="-113.44948"
- y2="-113.44948"
- gradientTransform="scale(0.99537112,1.0046504)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7915"
- x1="-294.82532"
- x2="-285.46247"
- xlink:href="#linearGradient11227"
- y1="-116.86081"
- y2="-116.86081"
- gradientTransform="scale(1.1393952,0.87765863)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7912"
- x1="-363.06313"
- x2="-354.09621"
- xlink:href="#linearGradient11227"
- y1="-145.94902"
- y2="-145.94902"
- gradientTransform="scale(0.85895117,1.1642105)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7909"
- x1="-328.67311"
- x2="-318.58887"
- xlink:href="#linearGradient11227"
- y1="-147.23473"
- y2="-147.23473"
- gradientTransform="scale(0.95434767,1.0478362)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7906"
- x1="-344.40168"
- x2="-334.691"
- xlink:href="#linearGradient11227"
- y1="-127.86438"
- y2="-127.86438"
- gradientTransform="scale(0.90626962,1.1034244)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7903"
- x1="-326.0562"
- x2="-316.88265"
- xlink:href="#linearGradient11227"
- y1="-121.86934"
- y2="-121.86934"
- gradientTransform="scale(0.96324014,1.0381627)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7900"
- x1="-286.92757"
- x2="-277.59508"
- xlink:href="#linearGradient11227"
- y1="-124.6788"
- y2="-124.6788"
- gradientTransform="scale(1.0959678,0.91243557)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7897"
- x1="-306.76444"
- x2="-298.61037"
- xlink:href="#linearGradient11227"
- y1="-104.78513"
- y2="-104.78513"
- gradientTransform="scale(1.0242863,0.97628949)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7894"
- x1="-250.70239"
- x2="-243.72243"
- xlink:href="#linearGradient11227"
- y1="-115.80395"
- y2="-115.80395"
- gradientTransform="scale(1.251796,0.79885223)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7891"
- x1="-238.51689"
- x2="-230.19642"
- xlink:href="#linearGradient11227"
- y1="-214.84672"
- y2="-214.84672"
- gradientTransform="scale(1.1953117,0.83660189)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7888"
- x1="-249.52166"
- x2="-241.81043"
- xlink:href="#linearGradient11227"
- y1="-198.44115"
- y2="-198.44115"
- gradientTransform="scale(1.0733809,0.93163571)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7885"
- x1="-222.95443"
- x2="-215.48126"
- xlink:href="#linearGradient11227"
- y1="-210.63488"
- y2="-210.63488"
- gradientTransform="scale(1.2011884,0.83250885)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7882"
- x1="-233.72752"
- x2="-224.28471"
- xlink:href="#linearGradient11227"
- y1="-207.89021"
- y2="-207.89021"
- gradientTransform="scale(1.2225684,0.81795013)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7879"
- x1="-248.17839"
- x2="-240.80645"
- xlink:href="#linearGradient11227"
- y1="-149.09611"
- y2="-149.09611"
- gradientTransform="scale(1.0580713,0.94511587)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7876"
- x1="-275.94091"
- x2="-267.78142"
- xlink:href="#linearGradient11227"
- y1="-131.14764"
- y2="-131.14764"
- gradientTransform="scale(0.88272748,1.1328525)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7873"
- x1="-267.03927"
- x2="-258.77915"
- xlink:href="#linearGradient11227"
- y1="-128.803"
- y2="-128.803"
- gradientTransform="scale(0.9908777,1.0092063)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7870"
- x1="-249.37562"
- x2="-240.79293"
- xlink:href="#linearGradient11227"
- y1="-134.6316"
- y2="-134.6316"
- gradientTransform="scale(0.98382489,1.016441)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7867"
- x1="-241.45083"
- x2="-234.21029"
- xlink:href="#linearGradient11227"
- y1="-130.21871"
- y2="-130.21871"
- gradientTransform="scale(1.0930698,0.91485465)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7864"
- x1="-249.71737"
- x2="-243.01881"
- xlink:href="#linearGradient11227"
- y1="-122.95112"
- y2="-122.95112"
- gradientTransform="scale(0.97731118,1.0232156)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7861"
- x1="-260.06034"
- x2="-253.11646"
- xlink:href="#linearGradient11227"
- y1="-107.26058"
- y2="-107.26058"
- gradientTransform="scale(1.0050568,0.99496863)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7858"
- x1="-264.85091"
- x2="-257.02238"
- xlink:href="#linearGradient11227"
- y1="-106.31137"
- y2="-106.31137"
- gradientTransform="scale(0.91982761,1.0871602)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7855"
- x1="-196.18726"
- x2="-188.76476"
- xlink:href="#linearGradient11227"
- y1="-132.34514"
- y2="-132.34514"
- gradientTransform="scale(1.3459825,0.74295169)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7852"
- x1="-161.20503"
- x2="-153.76854"
- xlink:href="#linearGradient11227"
- y1="-161.12888"
- y2="-161.12888"
- gradientTransform="scale(1.5396844,0.64948373)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7849"
- x1="-259.52919"
- x2="-252.42109"
- xlink:href="#linearGradient11227"
- y1="-90.006974"
- y2="-90.006974"
- gradientTransform="scale(1.0125682,0.98758781)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient7846"
- x1="-239.33089"
- x2="-233.36121"
- xlink:href="#linearGradient11227"
- y1="-96.893744"
- y2="-96.893744"
- gradientTransform="scale(1.0124729,0.98768074)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- gradientTransform="scale(1.012472,0.987681)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11291"
- x1="759.75531"
- x2="773.60297"
- xlink:href="#linearGradient11227"
- y1="543.72327"
- y2="543.72327" />
- <linearGradient
- gradientTransform="scale(1.012567,0.987588)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11289"
- x1="712.77844"
- x2="729.26685"
- xlink:href="#linearGradient11227"
- y1="559.77179"
- y2="559.77179" />
- <linearGradient
- gradientTransform="scale(1.539684,0.649484)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11287"
- x1="495.14441"
- x2="507.98087"
- xlink:href="#linearGradient11227"
- y1="794.8819"
- y2="794.8819" />
- <linearGradient
- gradientTransform="scale(1.345981,0.742952)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11285"
- x1="534.02057"
- x2="551.23828"
- xlink:href="#linearGradient11227"
- y1="716.31262"
- y2="716.31262" />
- <linearGradient
- gradientTransform="scale(0.919828,1.087160)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11283"
- x1="832.99451"
- x2="851.15399"
- xlink:href="#linearGradient11227"
- y1="451.5596"
- y2="451.5596" />
- <linearGradient
- gradientTransform="scale(1.005058,0.994967)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11281"
- x1="721.37079"
- x2="737.47821"
- xlink:href="#linearGradient11227"
- y1="514.04895"
- y2="514.04895" />
- <linearGradient
- gradientTransform="scale(0.977311,1.023216)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11279"
- x1="782.97034"
- x2="798.50873"
- xlink:href="#linearGradient11227"
- y1="456.59073"
- y2="456.59073" />
- <linearGradient
- gradientTransform="scale(1.093070,0.914855)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11277"
- x1="657.88141"
- x2="674.677"
- xlink:href="#linearGradient11227"
- y1="527.59485"
- y2="527.59485" />
- <linearGradient
- gradientTransform="scale(0.983825,1.016441)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11275"
- x1="774.74408"
- x2="794.65302"
- xlink:href="#linearGradient11227"
- y1="434.44052"
- y2="434.44052" />
- <linearGradient
- gradientTransform="scale(0.990878,1.009206)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11273"
- x1="724.13934"
- x2="743.30005"
- xlink:href="#linearGradient11227"
- y1="453.31421"
- y2="453.31421" />
- <linearGradient
- gradientTransform="scale(0.882728,1.132852)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11271"
- x1="868.10095"
- x2="887.0282"
- xlink:href="#linearGradient11227"
- y1="365.78714"
- y2="365.78714" />
- <linearGradient
- gradientTransform="scale(1.058071,0.945116)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11269"
- x1="682.56348"
- x2="699.66388"
- xlink:href="#linearGradient11227"
- y1="457.24191"
- y2="457.24191" />
- <linearGradient
- gradientTransform="scale(1.222567,0.817951)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11267"
- x1="546.78748"
- x2="568.69165"
- xlink:href="#linearGradient11227"
- y1="445.71585"
- y2="445.71585" />
- <linearGradient
- gradientTransform="scale(1.201187,0.832510)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11265"
- x1="591.15997"
- x2="608.49524"
- xlink:href="#linearGradient11227"
- y1="423.12045"
- y2="423.12045" />
- <linearGradient
- gradientTransform="scale(1.073382,0.931635)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11263"
- x1="661.50104"
- x2="679.38849"
- xlink:href="#linearGradient11227"
- y1="354.39905"
- y2="354.39905" />
- <linearGradient
- gradientTransform="scale(1.195314,0.836600)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11261"
- x1="560.50793"
- x2="579.80859"
- xlink:href="#linearGradient11227"
- y1="408.8916"
- y2="408.8916" />
- <linearGradient
- gradientTransform="scale(1.251797,0.798852)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11259"
- x1="481.98502"
- x2="498.17615"
- xlink:href="#linearGradient11227"
- y1="681.50909"
- y2="681.50909" />
- <linearGradient
- gradientTransform="scale(1.024285,0.976291)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11257"
- x1="588.16779"
- x2="607.08252"
- xlink:href="#linearGradient11227"
- y1="534.38354"
- y2="534.38354" />
- <linearGradient
- gradientTransform="scale(1.095968,0.912435)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11255"
- x1="549.17065"
- x2="570.81885"
- xlink:href="#linearGradient11227"
- y1="542.64679"
- y2="542.64679" />
- <linearGradient
- gradientTransform="scale(0.963240,1.038163)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11253"
- x1="625.78949"
- x2="647.06903"
- xlink:href="#linearGradient11227"
- y1="448.42059"
- y2="448.42059" />
- <linearGradient
- gradientTransform="scale(0.906270,1.103424)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11251"
- x1="670.11877"
- x2="692.64429"
- xlink:href="#linearGradient11227"
- y1="391.27255"
- y2="391.27255" />
- <linearGradient
- gradientTransform="scale(0.954348,1.047836)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11249"
- x1="632.5968"
- x2="655.98883"
- xlink:href="#linearGradient11227"
- y1="382.83206"
- y2="382.83206" />
- <linearGradient
- gradientTransform="scale(0.858951,1.164210)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11247"
- x1="707.75684"
- x2="728.55707"
- xlink:href="#linearGradient11227"
- y1="313.40659"
- y2="313.40659" />
- <linearGradient
- gradientTransform="scale(1.139395,0.877659)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11245"
- x1="484.55173"
- x2="506.07294"
- xlink:href="#linearGradient11227"
- y1="593.74213"
- y2="593.74213" />
- <linearGradient
- gradientTransform="scale(0.995371,1.004651)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11243"
- x1="552.79004"
- x2="577.53467"
- xlink:href="#linearGradient11227"
- y1="492.33859"
- y2="492.33859" />
- <linearGradient
- gradientTransform="scale(1.082118,0.924113)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11241"
- x1="508.7547"
- x2="539.5755"
- xlink:href="#linearGradient11227"
- y1="507.62125"
- y2="507.62125" />
- <linearGradient
- gradientTransform="scale(1.088869,0.918384)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11239"
- x1="509.09863"
- x2="534.97827"
- xlink:href="#linearGradient11227"
- y1="478.83255"
- y2="478.83255" />
- <linearGradient
- gradientTransform="scale(1.145371,0.873080)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11237"
- x1="480.67374"
- x2="505.72382"
- xlink:href="#linearGradient11227"
- y1="469.36761"
- y2="469.36761" />
- <linearGradient
- gradientTransform="scale(1.098865,0.910030)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11235"
- x1="501.20609"
- x2="529.56006"
- xlink:href="#linearGradient11227"
- y1="418.39951"
- y2="418.39951" />
- <linearGradient
- gradientTransform="scale(0.894007,1.118559)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient11233"
- x1="622.53632"
- x2="651.97101"
- xlink:href="#linearGradient11227"
- y1="307.72"
- y2="307.72" />
- <linearGradient
- gradientTransform="scale(1.137075,0.879450)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient9529"
- x1="581.94739"
- x2="600.62476"
- xlink:href="#linearGradient9523"
- y1="284.77969"
- y2="276.53366" />
- <linearGradient
- gradientTransform="scale(0.686891,1.455835)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient7987"
- x1="952.91315"
- x2="1170.6921"
- xlink:href="#linearGradient7981"
- y1="306.11334"
- y2="306.11334" />
- <linearGradient
- gradientTransform="scale(0.752677,1.328592)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient7219"
- x1="924.96478"
- x2="685.7934"
- xlink:href="#linearGradient7213"
- y1="332.74713"
- y2="334.59088" />
- <linearGradient
- gradientTransform="matrix(1.750587,0.000000,0.000000,0.822814,86.12883,-36.43990)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37848"
- x1="225.25496"
- x2="285.47906"
- xlink:href="#linearGradient25658"
- y1="801.65796"
- y2="801.65796" />
- <linearGradient
- gradientTransform="matrix(1.390113,0.000000,0.000000,0.719366,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37846"
- x1="624.01233"
- x2="568.19629"
- xlink:href="#linearGradient24104"
- y1="718.10217"
- y2="690.93042" />
- <linearGradient
- gradientTransform="matrix(1.667806,0.000000,0.000000,0.599590,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37844"
- x1="495.33688"
- x2="512.763"
- xlink:href="#linearGradient24874"
- y1="765.409"
- y2="822.45459" />
- <linearGradient
- gradientTransform="matrix(0.913548,0.000000,0.000000,1.094633,-288.5929,85.96114)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37842"
- x1="1016.853"
- x2="971.39398"
- xlink:href="#linearGradient24110"
- y1="445.55792"
- y2="467.96381" />
- <linearGradient
- gradientTransform="matrix(1.025713,0.000000,0.000000,0.974932,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37840"
- x1="409.46954"
- x2="408.96033"
- xlink:href="#linearGradient28785"
- y1="447.73465"
- y2="373.60046" />
- <linearGradient
- gradientTransform="matrix(1.759554,0.000000,0.000000,0.568326,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37838"
- x1="226.57547"
- x2="237.13167"
- xlink:href="#linearGradient28009"
- y1="833.93268"
- y2="847.99634" />
- <linearGradient
- gradientTransform="matrix(0.981909,0.000000,0.000000,1.018424,2.844940,-1.012422)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37836"
- x1="380.77408"
- x2="484.1637"
- xlink:href="#linearGradient28775"
- y1="425.61795"
- y2="425.61795" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37758"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37756"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37754"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37752"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37750"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37748"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37746"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37744"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- id="linearGradient37622"
- x1="221.80743"
- x2="312.09669"
- xlink:href="#linearGradient36801"
- y1="1357.9286"
- y2="1357.9286"
- gradientTransform="scale(1.8120928,0.55184813)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- gradientTransform="matrix(1.812093,0.000000,0.000000,0.551848,52.00000,131.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient37606"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- gradientTransform="scale(1.812093,0.551848)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient36807"
- x1="194.5396"
- x2="283.40054"
- xlink:href="#linearGradient36801"
- y1="1120.5447"
- y2="1120.5447" />
- <linearGradient
- id="linearGradient35252"
- x1="822.91337"
- x2="855.08716"
- xlink:href="#linearGradient33647"
- y1="352.00138"
- y2="353.5095"
- gradientTransform="scale(0.63385361,1.5776514)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient35250"
- x1="940.30913"
- x2="867.2213"
- xlink:href="#linearGradient32879"
- y1="266.50261"
- y2="356.6069"
- gradientTransform="scale(0.57492203,1.7393663)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient35248"
- x1="343.84308"
- x2="356.26609"
- xlink:href="#linearGradient31341"
- y1="693.50753"
- y2="744.71249"
- gradientTransform="scale(1.6261505,0.61494922)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient35246"
- x1="935.86488"
- x2="809.98511"
- xlink:href="#linearGradient31341"
- y1="357.88301"
- y2="356.86438"
- gradientTransform="scale(0.67096927,1.4903812)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- gradientTransform="matrix(0.670969,0.000000,0.000000,1.490381,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35213"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- gradientTransform="matrix(1.626151,0.000000,0.000000,0.614949,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35211"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="matrix(0.574922,0.000000,0.000000,1.739367,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35209"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="matrix(0.633854,0.000000,0.000000,1.577651,179.0000,128.0000)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient35207"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="scale(0.633854,1.577651)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient33653"
- x1="540.51343"
- x2="572.68719"
- xlink:href="#linearGradient33647"
- y1="270.86816"
- y2="272.37628" />
- <linearGradient
- gradientTransform="scale(0.574922,1.739367)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32885"
- x1="628.96259"
- x2="555.87469"
- xlink:href="#linearGradient32879"
- y1="192.89214"
- y2="282.72955" />
- <linearGradient
- gradientTransform="scale(1.626151,0.614949)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient32117"
- x1="233.76707"
- x2="246.19011"
- xlink:href="#linearGradient31341"
- y1="485.36044"
- y2="536.56543" />
- <linearGradient
- gradientTransform="scale(0.670969,1.490381)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient31347"
- x1="669.08685"
- x2="543.20709"
- xlink:href="#linearGradient31341"
- y1="271.99896"
- y2="270.98035" />
- <linearGradient
- id="linearGradient29655"
- x1="329.39266"
- x2="401.67184"
- xlink:href="#linearGradient25658"
- y1="908.97335"
- y2="908.97335"
- gradientTransform="scale(1.4586154,0.68558166)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient29653"
- x1="416.40836"
- x2="360.59228"
- xlink:href="#linearGradient24104"
- y1="837.59807"
- y2="810.42625"
- gradientTransform="scale(1.3901132,0.71936587)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient29651"
- x1="322.29958"
- x2="339.72568"
- xlink:href="#linearGradient24874"
- y1="908.77516"
- y2="965.82073"
- gradientTransform="scale(1.6678055,0.59959029)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient29649"
- x1="700.94939"
- x2="655.49035"
- xlink:href="#linearGradient24110"
- y1="524.08769"
- y2="546.49356"
- gradientTransform="scale(0.91354842,1.0946327)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient29599"
- x1="412.24318"
- x2="411.73402"
- xlink:href="#linearGradient28785"
- y1="446.69634"
- y2="372.56214"
- gradientTransform="scale(1.0257129,0.97493171)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient29597"
- x1="228.19232"
- x2="238.74852"
- xlink:href="#linearGradient28009"
- y1="832.15165"
- y2="846.21529"
- gradientTransform="scale(1.7595541,0.56832581)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient29595"
- x1="383.67145"
- x2="487.06107"
- xlink:href="#linearGradient28775"
- y1="424.62374"
- y2="424.62374"
- gradientTransform="scale(0.98190902,1.0184243)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21739"
- x1="577.9729"
- x2="522.1569"
- xlink:href="#linearGradient16059"
- y1="872.4047"
- y2="845.23296"
- gradientTransform="scale(1.3901129,0.71936601)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21737"
- x1="456.96336"
- x2="474.38945"
- xlink:href="#linearGradient15293"
- y1="950.53498"
- y2="1007.5805"
- gradientTransform="scale(1.6678052,0.59959039)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21735"
- x1="946.7961"
- x2="901.33711"
- xlink:href="#linearGradient15285"
- y1="546.96192"
- y2="569.36779"
- gradientTransform="scale(0.91354842,1.0946327)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21684"
- x1="416.09557"
- x2="429.2342"
- xlink:href="#linearGradient12202"
- y1="474.41301"
- y2="428.14508"
- gradientTransform="scale(1.1554976,0.86542805)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21682"
- x1="431.32265"
- x2="448.10612"
- xlink:href="#linearGradient11434"
- y1="439.96015"
- y2="466.27166"
- gradientTransform="scale(1.1426339,0.87517095)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21680"
- x1="389.27437"
- x2="353.62264"
- xlink:href="#linearGradient7622"
- y1="773.55718"
- y2="740.05778"
- gradientTransform="scale(1.5124615,0.66117385)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21678"
- x1="227.98634"
- x2="290.49919"
- xlink:href="#linearGradient7622"
- y1="970.66995"
- y2="937.32422"
- gradientTransform="scale(1.6856213,0.59325305)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21676"
- x1="214.67588"
- x2="276.69969"
- xlink:href="#linearGradient7622"
- y1="887.55999"
- y2="888.74532"
- gradientTransform="scale(1.7833645,0.56073785)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21674"
- x1="589.91995"
- x2="749.55737"
- xlink:href="#linearGradient3776"
- y1="439.09538"
- y2="439.09553"
- gradientTransform="scale(0.83626643,1.1957912)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21672"
- x1="559.76867"
- x2="380.26312"
- xlink:href="#linearGradient3006"
- y1="468.73474"
- y2="471.12037"
- gradientTransform="scale(0.89912996,1.1121863)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21601"
- x1="640.42505"
- x2="621.68993"
- xlink:href="#linearGradient8812"
- y1="176.68761"
- y2="195.22758"
- gradientTransform="scale(0.76136088,1.3134376)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21599"
- x1="479.76693"
- x2="505.00608"
- xlink:href="#linearGradient7274"
- y1="313.04719"
- y2="332.7672"
- gradientTransform="scale(1.0948941,0.91333036)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21597"
- x1="336.63582"
- x2="405.66187"
- xlink:href="#linearGradient7274"
- y1="257.44549"
- y2="303.05395"
- gradientTransform="scale(1.152955,0.86733655)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21595"
- x1="404.72604"
- x2="419.90511"
- xlink:href="#linearGradient6500"
- y1="323.95455"
- y2="318.75136"
- gradientTransform="scale(1.1940326,0.8374981)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21593"
- x1="506.86686"
- x2="545.43829"
- xlink:href="#linearGradient5730"
- y1="256.20581"
- y2="229.08536"
- gradientTransform="scale(0.96209128,1.0394024)"
- gradientUnits="userSpaceOnUse" />
- <radialGradient
- cx="294.70374"
- cy="206.08632"
- fx="294.70374"
- fy="206.08632"
- gradientTransform="matrix(0.881013,0,0,0.543591,183.576,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient21591"
- r="22.642897"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <radialGradient
- cx="428.68643"
- cy="87.624062"
- fx="428.68643"
- fy="87.624062"
- gradientTransform="matrix(0.813869,0,0,0.588437,183.576,167.2354)"
- gradientUnits="userSpaceOnUse"
- id="radialGradient21589"
- r="27.344202"
- spreadMethod="pad"
- xlink:href="#linearGradient4166" />
- <linearGradient
- id="linearGradient21520"
- x1="151.75999"
- x2="170.84677"
- xlink:href="#linearGradient3217"
- y1="487.57394"
- y2="484.68043"
- gradientTransform="scale(0.76507718,1.3070577)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21518"
- x1="141.0036"
- x2="145.67162"
- xlink:href="#linearGradient2431"
- y1="531.67086"
- y2="527.38208"
- gradientTransform="scale(0.87643718,1.1409831)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- id="linearGradient21516"
- x1="151.35096"
- x2="128.67909"
- xlink:href="#linearGradient2416"
- y1="533.55611"
- y2="545.69902"
- gradientTransform="scale(0.89167507,1.1214848)"
- gradientUnits="userSpaceOnUse" />
- <linearGradient
- gradientTransform="matrix(0.963841,0.000000,0.000000,1.037516,-3.429160,6.573687)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient6337"
- x1="303.96457"
- x2="349.80203"
- xlink:href="#linearGradient6331"
- y1="538.76196"
- y2="598.16534" />
- <linearGradient
- gradientTransform="scale(0.917548,1.089862)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient3952"
- spreadMethod="reflect"
- x1="135.21931"
- x2="345.67752"
- xlink:href="#linearGradient3942"
- y1="465.45325"
- y2="465.45325" />
- <linearGradient
- gradientTransform="scale(1.794447,0.557275)"
- gradientUnits="userSpaceOnUse"
- id="linearGradient2324"
- x1="-44.860626"
- x2="427.30051"
- xlink:href="#linearGradient2318"
- y1="1442.5029"
- y2="1442.5029" />
- <pattern
- height="261"
- id="pattern1501"
- patternTransform="translate(-80.00000,673.3622)"
- patternUnits="userSpaceOnUse"
- width="846" />
- <linearGradient
- id="linearGradient2318">
- <stop
- id="stop2320"
- offset="0"
- stop-color="#000000" />
- <stop
- id="stop2322"
- offset="1"
- stop-opacity="0"
- stop-color="#000000" />
- </linearGradient>
- <linearGradient
- id="linearGradient3942">
- <stop
- id="stop3944"
- offset="0"
- stop-color="#000000" />
- <stop
- id="stop3950"
- offset="0"
- stop-opacity="0.498039"
- stop-color="#000000" />
- <stop
- id="stop3946"
- offset="1"
- stop-opacity="0"
- stop-color="#000000" />
- </linearGradient>
- <linearGradient
- id="linearGradient6331">
- <stop
- id="stop6333"
- offset="0"
- stop-color="#bfbfbf" />
- <stop
- id="stop6335"
- offset="1"
- stop-opacity="0"
- stop-color="#bfbfbf" />
- </linearGradient>
- <linearGradient
- id="linearGradient2416">
- <stop
- id="stop2418"
- offset="0"
- stop-color="#bacfd1" />
- <stop
- id="stop2420"
- offset="1"
- stop-opacity="0"
- stop-color="#bacfd1" />
- </linearGradient>
- <linearGradient
- id="linearGradient2431">
- <stop
- id="stop2433"
- offset="0"
- stop-color="#8ba5a7" />
- <stop
- id="stop2435"
- offset="1"
- stop-opacity="0"
- stop-color="#91a4a5" />
- </linearGradient>
- <linearGradient
- id="linearGradient3217">
- <stop
- id="stop3219"
- offset="0"
- stop-color="#8ba2a2" />
- <stop
- id="stop3221"
- offset="1"
- stop-opacity="0"
- stop-color="#8ba2a2" />
- </linearGradient>
- <linearGradient
- id="linearGradient4166">
- <stop
- id="stop4168"
- offset="0"
- stop-color="#f0e095" />
- <stop
- id="stop4170"
- offset="1"
- stop-color="#b7a860" />
- </linearGradient>
- <linearGradient
- id="linearGradient5730">
- <stop
- id="stop5732"
- offset="0"
- stop-color="#7f97ae" />
- <stop
- id="stop5734"
- offset="1"
- stop-opacity="0"
- stop-color="#7f97ae" />
- </linearGradient>
- <linearGradient
- id="linearGradient6500">
- <stop
- id="stop6502"
- offset="0"
- stop-color="#66798c" />
- <stop
- id="stop6504"
- offset="1"
- stop-opacity="0"
- stop-color="#66798c" />
- </linearGradient>
- <linearGradient
- id="linearGradient7274">
- <stop
- id="stop7276"
- offset="0"
- stop-color="#7e75a2" />
- <stop
- id="stop7278"
- offset="1"
- stop-opacity="0"
- stop-color="#7e75a2" />
- </linearGradient>
- <linearGradient
- id="linearGradient8812">
- <stop
- id="stop8814"
- offset="0"
- stop-color="#a7d6ff" />
- <stop
- id="stop8816"
- offset="1"
- stop-opacity="0"
- stop-color="#a7d6ff" />
- </linearGradient>
- <linearGradient
- id="linearGradient3006">
- <stop
- id="stop3008"
- offset="0"
- stop-color="#f5eecd" />
- <stop
- id="stop3010"
- offset="1"
- stop-opacity="0"
- stop-color="#f5eecd" />
- </linearGradient>
- <linearGradient
- id="linearGradient3776">
- <stop
- id="stop3778"
- offset="0"
- stop-color="#a19c82" />
- <stop
- id="stop3780"
- offset="1"
- stop-opacity="0"
- stop-color="#e9d889" />
- </linearGradient>
- <linearGradient
- id="linearGradient6092">
- <stop
- id="stop6094"
- offset="0"
- stop-color="#a2a37c" />
- <stop
- id="stop6096"
- offset="1"
- stop-opacity="0"
- stop-color="#a2a37c" />
- </linearGradient>
- <linearGradient
- id="linearGradient7622">
- <stop
- id="stop7624"
- offset="0"
- stop-opacity="0.469388"
- stop-color="#183f70" />
- <stop
- id="stop7626"
- offset="1"
- stop-opacity="0.081633"
- stop-color="#61d5fa" />
- </linearGradient>
- <linearGradient
- id="linearGradient11434">
- <stop
- id="stop11436"
- offset="0"
- stop-color="#c3d5d7" />
- <stop
- id="stop11438"
- offset="1"
- stop-opacity="0"
- stop-color="#c3d5d7" />
- </linearGradient>
- <linearGradient
- id="linearGradient12202">
- <stop
- id="stop12204"
- offset="0"
- stop-color="#8ba5a7" />
- <stop
- id="stop12206"
- offset="1"
- stop-opacity="0"
- stop-color="#8ba5a7" />
- </linearGradient>
- <linearGradient
- id="linearGradient15285">
- <stop
- id="stop15287"
- offset="0"
- stop-color="#8ca8ae" />
- <stop
- id="stop15289"
- offset="1"
- stop-opacity="0"
- stop-color="#8ca8ae" />
- </linearGradient>
- <linearGradient
- id="linearGradient15293">
- <stop
- id="stop15295"
- offset="0"
- stop-color="#476e7a" />
- <stop
- id="stop15297"
- offset="1"
- stop-opacity="0"
- stop-color="#e0e7e9" />
- </linearGradient>
- <linearGradient
- id="linearGradient16059">
- <stop
- id="stop16061"
- offset="0"
- stop-opacity="0.796078"
- stop-color="#e1eaeb" />
- <stop
- id="stop16063"
- offset="1"
- stop-opacity="0.744898"
- stop-color="#9bb4bc" />
- </linearGradient>
- <linearGradient
- id="linearGradient24104">
- <stop
- id="stop24106"
- offset="0"
- stop-opacity="0.796078"
- stop-color="#ffffff" />
- <stop
- id="stop24108"
- offset="1"
- stop-opacity="0.734694"
- stop-color="#828282" />
- </linearGradient>
- <linearGradient
- id="linearGradient24110">
- <stop
- id="stop24112"
- offset="0"
- stop-opacity="0.744898"
- stop-color="#7d7d7d" />
- <stop
- id="stop24114"
- offset="1"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient24874">
- <stop
- id="stop24876"
- offset="0"
- stop-color="#686868" />
- <stop
- id="stop24878"
- offset="1"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient25658">
- <stop
- id="stop25660"
- offset="0"
- stop-color="#aeaeae" />
- <stop
- id="stop25662"
- offset="1"
- stop-opacity="0"
- stop-color="#aeaeae" />
- </linearGradient>
- <linearGradient
- id="linearGradient28009">
- <stop
- id="stop28011"
- offset="0"
- stop-color="#8e8e8e" />
- <stop
- id="stop28013"
- offset="1"
- stop-opacity="0"
- stop-color="#8e8e8e" />
- </linearGradient>
- <linearGradient
- id="linearGradient28775">
- <stop
- id="stop28777"
- offset="0"
- stop-color="#949494" />
- <stop
- id="stop28779"
- offset="1"
- stop-color="#cecece" />
- </linearGradient>
- <linearGradient
- id="linearGradient28785">
- <stop
- id="stop28787"
- offset="0"
- stop-color="#6d6d6d" />
- <stop
- id="stop28789"
- offset="1"
- stop-opacity="0"
- stop-color="#6d6d6d" />
- </linearGradient>
- <linearGradient
- id="linearGradient31333">
- <stop
- id="stop31335"
- offset="0"
- stop-opacity="0"
- stop-color="#000000" />
- <stop
- id="stop31337"
- offset="1"
- stop-opacity="0.408163"
- stop-color="#000000" />
- </linearGradient>
- <linearGradient
- id="linearGradient31341">
- <stop
- id="stop31343"
- offset="0"
- stop-color="#3d534d" />
- <stop
- id="stop31345"
- offset="1"
- stop-opacity="0"
- stop-color="#61847b" />
- </linearGradient>
- <linearGradient
- id="linearGradient32879">
- <stop
- id="stop32881"
- offset="0"
- stop-color="#9dc3b9" />
- <stop
- id="stop32883"
- offset="1"
- stop-opacity="0"
- stop-color="#7daa9e" />
- </linearGradient>
- <linearGradient
- id="linearGradient33647">
- <stop
- id="stop33649"
- offset="0"
- stop-color="#41948e" />
- <stop
- id="stop33651"
- offset="1"
- stop-opacity="0"
- stop-color="#41948e" />
- </linearGradient>
- <linearGradient
- id="linearGradient36801">
- <stop
- id="stop36803"
- offset="0"
- stop-color="#214965" />
- <stop
- id="stop36805"
- offset="1"
- stop-opacity="0"
- stop-color="#2f6891" />
- </linearGradient>
- <linearGradient
- id="linearGradient2408">
- <stop
- id="stop2410"
- offset="0"
- stop-color="#007759" />
- <stop
- id="stop2412"
- offset="1"
- stop-color="#00a97e" />
- </linearGradient>
- <linearGradient
- id="linearGradient7213">
- <stop
- id="stop7215"
- offset="0"
- stop-color="#ffe0ab" />
- <stop
- id="stop7217"
- offset="1"
- stop-opacity="0"
- stop-color="#ffe0ab" />
- </linearGradient>
- <linearGradient
- id="linearGradient7981">
- <stop
- id="stop7983"
- offset="0"
- stop-color="#baa589" />
- <stop
- id="stop7985"
- offset="1"
- stop-opacity="0"
- stop-color="#baa589" />
- </linearGradient>
- <linearGradient
- id="linearGradient9523">
- <stop
- id="stop9525"
- offset="0"
- stop-color="#8ba5a7" />
- <stop
- id="stop9527"
- offset="1"
- stop-opacity="0"
- stop-color="#8ba5a7" />
- </linearGradient>
- <linearGradient
- id="linearGradient11227">
- <stop
- id="stop11229"
- offset="0"
- stop-color="#ffffff" />
- <stop
- id="stop11231"
- offset="1"
- stop-opacity="0"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient9573">
- <stop
- id="stop9575"
- offset="0"
- stop-color="#ff9a56" />
- <stop
- id="stop9577"
- offset="1"
- stop-opacity="0"
- stop-color="#ff9a56" />
- </linearGradient>
- <linearGradient
- id="linearGradient10342">
- <stop
- id="stop10344"
- offset="0"
- stop-color="#ffa362" />
- <stop
- id="stop10346"
- offset="1"
- stop-opacity="0"
- stop-color="#ffa362" />
- </linearGradient>
- <linearGradient
- id="linearGradient11110">
- <stop
- id="stop11112"
- offset="0"
- stop-color="#d8cb92" />
- <stop
- id="stop11114"
- offset="1"
- stop-opacity="0"
- stop-color="#d8cb92" />
- </linearGradient>
- <linearGradient
- id="linearGradient11878">
- <stop
- id="stop11880"
- offset="0"
- stop-color="#ffec8f" />
- <stop
- id="stop11882"
- offset="1"
- stop-opacity="0"
- stop-color="#ffec8f" />
- </linearGradient>
- <linearGradient
- id="linearGradient14218">
- <stop
- id="stop14220"
- offset="0"
- stop-color="#ffffff" />
- <stop
- id="stop14222"
- offset="1"
- stop-opacity="0"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient3536">
- <stop
- id="stop3538"
- offset="0"
- stop-color="#785d3f" />
- <stop
- id="stop3540"
- offset="1"
- stop-opacity="0"
- stop-color="#785d3f" />
- </linearGradient>
- <linearGradient
- id="linearGradient4304">
- <stop
- id="stop4306"
- offset="0"
- stop-color="#ddb890" />
- <stop
- id="stop4308"
- offset="1"
- stop-opacity="0"
- stop-color="#ddb890" />
- </linearGradient>
- <linearGradient
- id="linearGradient5072">
- <stop
- id="stop5074"
- offset="0"
- stop-color="#c8b197" />
- <stop
- id="stop5076"
- offset="1"
- stop-opacity="0"
- stop-color="#c8b197" />
- </linearGradient>
- <linearGradient
- id="linearGradient7375">
- <stop
- id="stop7377"
- offset="0"
- stop-color="#6adfff" />
- <stop
- id="stop7379"
- offset="1"
- stop-color="#40869a" />
- </linearGradient>
- <linearGradient
- id="linearGradient14415">
- <stop
- id="stop14417"
- offset="0"
- stop-color="#b20000" />
- <stop
- id="stop14419"
- offset="1"
- stop-opacity="0"
- stop-color="#b20000" />
- </linearGradient>
- <linearGradient
- id="linearGradient14425">
- <stop
- id="stop14427"
- offset="0"
- stop-color="#ce2724" />
- <stop
- id="stop14429"
- offset="1"
- stop-opacity="0"
- stop-color="#ce2724" />
- </linearGradient>
- <linearGradient
- id="linearGradient14435">
- <stop
- id="stop14437"
- offset="0"
- stop-color="#ce2724" />
- <stop
- id="stop14439"
- offset="1"
- stop-opacity="0"
- stop-color="#ce2724" />
- </linearGradient>
- <linearGradient
- id="linearGradient15239">
- <stop
- id="stop15241"
- offset="0"
- stop-color="#ffe3e3" />
- <stop
- id="stop15243"
- offset="1"
- stop-opacity="0"
- stop-color="#ffe3e3" />
- </linearGradient>
- <linearGradient
- id="linearGradient7562">
- <stop
- id="stop7564"
- offset="0"
- stop-color="#e8e8e8" />
- <stop
- id="stop7566"
- offset="1"
- stop-opacity="0"
- stop-color="#000000" />
- </linearGradient>
- <linearGradient
- id="linearGradient8380">
- <stop
- id="stop8382"
- offset="0"
- stop-color="#ffffd8" />
- <stop
- id="stop8384"
- offset="1"
- stop-color="#cb0000" />
- </linearGradient>
- <linearGradient
- id="linearGradient5296">
- <stop
- id="stop5298"
- offset="0"
- stop-color="#e4b270" />
- <stop
- id="stop5300"
- offset="1"
- stop-opacity="0"
- stop-color="#e4b270" />
- </linearGradient>
- <linearGradient
- id="linearGradient6114">
- <stop
- id="stop6116"
- offset="0"
- stop-color="#a3d7ff" />
- <stop
- id="stop6118"
- offset="1"
- stop-opacity="0"
- stop-color="#a3d7ff" />
- </linearGradient>
- <linearGradient
- id="linearGradient6930">
- <stop
- id="stop6932"
- offset="0"
- stop-color="#447aa6" />
- <stop
- id="stop6934"
- offset="1"
- stop-opacity="0"
- stop-color="#447aa6" />
- </linearGradient>
- <linearGradient
- id="linearGradient8564">
- <stop
- id="stop8566"
- offset="0"
- stop-color="#e4b270" />
- <stop
- id="stop8568"
- offset="1"
- stop-opacity="0"
- stop-color="#e4b270" />
- </linearGradient>
- <linearGradient
- id="linearGradient11001">
- <stop
- id="stop11003"
- offset="0"
- stop-color="#ffffff" />
- <stop
- id="stop11005"
- offset="1"
- stop-opacity="0"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient11011">
- <stop
- id="stop11013"
- offset="0"
- stop-color="#ffffff" />
- <stop
- id="stop11015"
- offset="1"
- stop-opacity="0"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient11829">
- <stop
- id="stop11831"
- offset="0"
- stop-color="#ffffff" />
- <stop
- id="stop11833"
- offset="1"
- stop-opacity="0"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient13619">
- <stop
- id="stop13621"
- offset="0"
- stop-color="#679400" />
- <stop
- id="stop13623"
- offset="1"
- stop-color="#b2ff00" />
- </linearGradient>
- <linearGradient
- id="linearGradient15247">
- <stop
- id="stop15249"
- offset="0"
- stop-color="#70a100" />
- <stop
- id="stop15251"
- offset="1"
- stop-color="#b1ff00" />
- </linearGradient>
- <linearGradient
- id="linearGradient16064">
- <stop
- id="stop16066"
- offset="0"
- stop-color="#74a700" />
- <stop
- id="stop16068"
- offset="1"
- stop-color="#96d800" />
- </linearGradient>
- <linearGradient
- id="linearGradient20407">
- <stop
- id="stop20409"
- offset="0"
- stop-color="#ffffff" />
- <stop
- id="stop20411"
- offset="1"
- stop-opacity="0"
- stop-color="#ffffff" />
- </linearGradient>
- <linearGradient
- id="linearGradient22180">
- <stop
- id="stop22182"
- offset="0"
- stop-color="#9fcff5" />
- <stop
- id="stop22184"
- offset="1"
- stop-opacity="0"
- stop-color="#9fcff5" />
- </linearGradient>
- <linearGradient
- id="linearGradient22998">
- <stop
- id="stop23000"
- offset="0"
- stop-color="#6db8f2" />
- <stop
- id="stop23002"
- offset="1"
- stop-opacity="0"
- stop-color="#a2d1f5" />
- </linearGradient>
- <linearGradient
- id="linearGradient23816">
- <stop
- id="stop23818"
- offset="0"
- stop-color="#a9d4f6" />
- <stop
- id="stop23820"
- offset="1"
- stop-opacity="0"
- stop-color="#a9d4f6" />
- </linearGradient>
- <linearGradient
- id="linearGradient24634">
- <stop
- id="stop24636"
- offset="0"
- stop-color="#bddef9" />
- <stop
- id="stop24638"
- offset="1"
- stop-opacity="0"
- stop-color="#bddef9" />
- </linearGradient>
- <linearGradient
- id="linearGradient32063">
- <stop
- id="stop32065"
- offset="0"
- stop-color="#f9f0d0" />
- <stop
- id="stop32067"
- offset="1"
- stop-opacity="0"
- stop-color="#f9f0d0" />
- </linearGradient>
- </defs>
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.67847364"
- inkscape:cx="372.04724"
- inkscape:cy="526.18109"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="1422"
- inkscape:window-height="931"
- inkscape:window-x="25"
- inkscape:window-y="25"
- inkscape:window-maximized="0" />
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <g
- id="g6518"
- transform="matrix(0.4115539,0,0,0.54478784,139.72753,109.14248)">
- <title
- id="title6520">Layer 1</title>
- <g
- id="layer6"
- display="none"
- style="display:none">
- <g
- id="g18440"
- transform="matrix(0.530838,0,0,0.530838,-512.0372,-412.8984)">
- <path
- d="m 855,411.52304 c 0,0.76193 0,0.76193 0,0 0,-3.02118 3.76917,13.879 8,26.57144 1.52264,4.568 -1,9.33646 -1,14 0,6.75135 0.47253,8.78964 -8,10 -5.30548,0.75794 -7.57074,4.35611 -10,8 -2.57056,3.85578 -0.10791,9.6619 -3,14 -3.19782,4.79676 -5.30102,11.90299 -7,17 -2.46771,7.40311 -8.16907,10.37027 -15.42859,14 -10.37421,5.18714 -15.05304,14.72858 -22.14282,23 -2.75366,3.21259 -12.18964,4 -16.42859,4 -5.97565,0 -10.88062,-0.12579 -17,-1 -7.1756,-1.02508 -14.17139,-6.54059 -21.57141,-7.42859 -7.42725,-0.89123 -14.26062,-1.54742 -21.42859,-2.57141 -7.95129,-1.13592 -12.46704,-6.49566 -17,-12 -4.44,-5.39142 -6.56873,-10.40005 -5,-17.57144 1.49622,-6.83997 3.69177,-14.04523 7,-20 3.47278,-6.25104 6.19257,-13.58301 9,-20 2.3172,-5.29645 6.56982,-9.99731 9.57141,-15 2.62897,-4.38153 -5.58136,-6.38238 -7.14282,-7.85712 -0.70099,-0.66208 -1.61908,-1.04763 -2.42859,-1.57144 -0.19989,-0.12933 -0.28571,-0.38095 -0.42859,-0.57144 C 709.17584,430.66226 709,429.48993 709,421.09448 c 0,-8.43622 -2.59027,-14.22919 0,-22 2.99799,-8.99389 5.95319,-6.06381 13,-9 7.19806,-2.99917 14.90985,-4 23,-4 9.43414,0 30.78607,-4.14261 40,2 1.38678,0.9245 2.66669,2 4,3 5.00177,3.75131 12.1568,5.65857 17.42859,10 4.81567,3.96585 14.85248,1.61826 20.57141,2.57144 5.6026,0.93375 10.76697,2.42856 16,2.42856 6.27399,0 8.55573,-0.47595 12,5.42856 z"
- id="path11377"
- inkscape:connector-curvature="0"
- style="fill:#ff6b63;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 845,468.09448 c -0.90985,3.28171 -1.42859,10.45761 -1.42859,15.57144 0,1.7345 -6.53338,10.22886 -7.14282,13.42856 -0.88074,4.62387 -9.79407,10.93689 -12.85718,14 -5.06872,5.06879 -8.40546,11.88935 -14.57141,16 -5.46057,3.64038 -9.64929,9.97724 -15.42859,12 -4.80237,1.68085 -6.78271,5 -12.57141,5 -5.91901,0 -8.44055,-2.32171 -10,-7 -1.46625,-4.39874 -1.89294,-11.82123 0.42859,-16 3.75488,-6.75885 4.94818,-8.03259 11.57141,-13 4.82825,-3.62121 9.08508,-10.08511 13.42859,-14.42856 5.06769,-5.06768 3.33758,-13.33761 8.57141,-18.57144 3.60199,-3.60202 6.7196,-5.57321 11,-7 4.52277,-1.5076 7.15076,-2 14,-2 3.80951,0 7.61908,0 11.42859,0 1.36444,0 2.38092,1.33335 3.57141,2 z"
- id="path13655"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18478);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 851.42859,409.09448 c 2.80237,2.67996 7.1322,9.34119 9.57141,13 1.31042,1.96564 0,10.39026 0,13 0,5.4162 2,10.33115 2,16 0,6.07965 -5.12769,7.02884 -9.42859,8 -6.16241,1.39151 -13.63641,0.6182 -18.57141,2 -1.55609,0.4357 -17.57001,-1 -7,-1 6.22748,0 -1.91345,-9.80795 -1.57141,-13 0.66369,-6.1947 6.93524,-8.63617 0,-15.57144 C 825.11279,430.20728 826,418.44598 826,416.09448 c 0,-6.99225 -4.6803,-8.0993 2.57141,-11 8.67529,-3.47009 19.65802,-0.47879 25,7 2.81763,3.94464 4.73248,8.12738 5.42859,13 0.0703,0.49216 -0.28571,0.95239 -0.42859,1.42856"
- id="path14423"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18480);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 752,409.09448 c -0.7619,0 -0.7619,0 0,0 5.65686,0 -8.86212,7.29321 -12,12 -2.62341,3.93509 -6.96503,5.31003 -11,8 -1.96735,1.31156 -4.74805,8.39035 -6,10 -3.57513,4.59662 -8.52887,0.4711 -11.57141,-2.57144 -4.82178,-4.82172 -4.5426,-6.42856 2.57141,-6.42856 0.11585,0 -5.7746,-8.57144 3,-8.57144 5.4256,0 -7.92914,-10.16083 11,-5.42856 0.91235,0.22809 2.66968,-9.57144 7,-9.57144 11.36346,0 5.3432,-0.20962 12,-2.42856 2.23608,-0.74536 3.33331,3.33335 5,5 z"
- id="path14433"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18482);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 750,390.09448 c -1.29157,0.86106 -13.89966,-1.3081 -18.57141,0 -3.7768,1.0575 -4.73126,2.77869 -7.42859,5 -3.36853,2.77411 -6.29614,1.88837 -8,7 -2.57306,7.71921 4.255,1.16333 6,0 3.66394,-2.44263 8.3269,-4.17123 11,-5.57144 2.95868,-1.54977 12.28949,-0.42856 15.57141,-0.42856 2.61908,0 5.2381,0 7.85718,0 0.22662,0 -5.74268,-5.18341 -6.42859,-6 z"
- id="path14445"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18484);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 728,519.09448 c 2.95276,0 -5.12293,-3.14923 -7,-5.42859 -3.74036,-4.54184 -9.58594,-7.75335 -13,-12.14285 -2.72003,-3.49713 0.30536,-7.36694 -4.42859,-5 -0.1687,0.0844 -1.43323,9.60513 -1.57141,10 -0.61273,1.75064 3.18274,6.26724 4,7.14285 2.60339,2.78937 4.61261,2.65424 8,4.42859 4.00745,2.09912 13.12415,1 17.42859,1 0.5238,0 1.04761,0 1.57141,0"
- id="path14447"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18486);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 793,453.66592 c -2.49927,-2.33749 -7.54132,-8.26565 -11,-10.57144 -4.41235,-2.94159 -9.78821,-1.74453 -15,-1 -3.48383,0.49768 -9.64423,1.67548 -13.42859,3 -4.49329,1.57266 -10.49579,5.93427 -14.57141,8.57144 -5.00177,3.23645 -7.94165,6.20606 -10.42859,11.42856 -2.39606,5.03177 -2.14282,10.23871 -2.14282,16 0,7.38135 6.56488,13.70871 11.57141,18 5.54315,4.75131 8.34442,5 15.57141,5 3.04981,0 11.13751,-3.86139 14.42859,-5.42856 4.76208,-2.26767 10.3606,-5.14517 14,-7.57144 4.16882,-2.77923 8.86688,-7.82025 11.57141,-12 1.74316,-2.69397 0.42859,-9.88751 0.42859,-13 0,-3.10208 -0.42859,-5.94473 -0.42859,-9"
- id="path15249"
- inkscape:connector-curvature="0"
- style="fill:#ffdca6;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 775,462.09448 c 0,-3.04269 -8.98456,-5.57144 -13.57141,-5.57144 -5.84497,0 -6.96564,0.73938 -10,3.57144 -2.5622,2.39136 -3.42859,5.8606 -3.42859,10 0,4.66571 7.01636,9.00458 10.57141,10 5.64887,1.5817 11.41553,1.18192 15,-3 C 776.35937,473.84186 776,470.6604 776,466.52304 c 0,-0.36264 -0.66669,-0.28571 -1,-0.42856"
- id="path16009"
- inkscape:connector-curvature="0"
- style="fill:#e4b26e;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 775,447.09448 c -0.29065,-1.03808 -2.5426,-9.4331 -6,-4 -1.75818,2.76279 -1.03223,5.57743 1,7 0.4762,0.33335 0.95239,0.66666 1.42859,1 4.88031,3.41623 2.84363,-5.45559 2.57141,-6"
- id="path17599"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 786,452.09448 c 0.6051,0.42356 -6.78168,-3.8732 -8.5,3 -0.85138,3.40546 6.89899,4.42069 8.92859,3 3.8977,-2.72839 -2.52741,-6.59967 -3.92859,-7"
- id="path17601"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 790,466.09448 c 1.53381,-1.07367 -7,-6.08838 -7,3 0,2.11853 7,3.48804 7,-0.42856 0,-3.55624 0.81287,-5.36822 -2,-6.07144"
- id="path17603"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 786,477.09448 c 0,-2.78698 -6,-2.0982 -6,5.5 0,3.95063 4.67792,4.62732 6.42859,-1.5 0.98077,-3.43277 -2.84253,-6.72406 -5.42859,-5"
- id="path17605"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 774,487.09448 c 0.0701,-0.24527 -6,-2.61117 -6,2 0,2.61038 3.99591,10.51425 5.57141,5 1.08856,-3.80993 -0.16016,-6.20691 -3.57141,-7.57144"
- id="path17607"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 762,491.52304 c 1.27673,-4.46847 -10.34875,-2.73105 -12,0.57144 -1.19116,2.38239 7.00427,8.73816 8.57141,4.42856 1.3877,-3.81604 3.10217,-7.09323 -1.57141,-8.42856"
- id="path17609"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 750,488.09448 c 0.18359,-0.50482 -6.54669,-5.90655 -9,-1 -2.19214,4.38422 1.53522,9.23239 6,7 4.68768,-2.34384 2.6532,-6.12903 0.5,-9"
- id="path17611"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 741,476.09448 c 1.52667,-0.76337 -7,-2.69781 -7,2 0,6.04642 6.5542,2 7.42859,2 0.9541,0 3.58136,-6.67331 -0.42859,-4 z"
- id="path17613"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 744,460.09448 c 2.48517,0 -9.30756,-4.07635 -7,4 0.20477,0.71668 0.66669,1.33335 1,2 1.3266,2.65323 4.92419,2.20844 5.57141,0.42856 C 743.75397,466.021 744,461.06024 744,460.09448 z"
- id="path17615"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 760,448.09448 c 1.15814,-3.18481 -9.02399,-1.96402 -11,1 -5.26679,7.90015 8.54364,7.15213 12,6 3.8941,-1.29803 -3.41162,-8.14969 -4.57141,-8.57144"
- id="path17617"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 775,443.66592 c 0.80939,-0.2698 -7.07452,-4.17929 -11,1.42856 -2.4892,3.55603 7.48566,7 9.57141,7 5.81091,0 1.89734,-8.42856 -1.57141,-8.42856"
- id="path17619"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.28506799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 848.09454,465.53369 c -3.72564,0.83502 -5.12146,1.93427 3.8512,-3.04636 1.96783,-1.09237 -3.31745,3.15326 -4.38477,5.1348 -1.479,6.00766 -0.71509,12.01617 -2.14197,18.06613 -1.48162,6.5304 -4.4234,12.22867 -8.14605,17.71115 -4.39722,5.8635 -10.51282,10.06501 -16.02661,14.79651 -6.7229,5.97162 -13.47461,11.91852 -20.11127,17.98761 -6.13586,5.85382 -13.06964,11.52002 -21.68823,12.58209 -4.89771,-0.20495 -9.50043,-2.07348 -14.12354,-3.54113 -4.55969,-1.88855 -9.40197,-2.41712 -14.19745,-3.30988 -6.02093,-0.97967 -11.87554,-2.68939 -17.77014,-4.22729 -6.12298,-1.37812 -11.96899,-3.39191 -17.80853,-5.6297 -4.98834,-1.44287 -9.77606,-3.36316 -14.50757,-5.47376 -4.47613,-2.44952 -6.75244,-6.9693 -8.7417,-11.50177 -2.10064,-4.97287 -1.48486,-9.98706 -0.58032,-15.13278 0.78906,-6.78156 3.58258,-12.89508 6.37567,-19.0448 2.91736,-5.36325 5.68292,-10.80005 8.44531,-16.25082 2.54743,-4.85968 6.05829,-9.10932 9.11115,-13.65183 1.80823,-2.98602 3.73236,-5.89227 5.72546,-8.75665 l 7.39826,-3.23297 c -1.93238,2.89755 -3.90528,5.76104 -5.71393,8.73935 -3.02484,4.56835 -6.56873,8.77972 -9.17578,13.60989 -2.75434,5.48416 -5.61212,10.90622 -8.61383,16.25888 -2.84143,6.02795 -5.49927,12.11142 -6.3349,18.78931 -0.83075,5.0047 -1.5163,9.83316 0.45703,14.69394 1.91839,4.36276 3.99811,8.76437 8.45093,10.94998 4.6593,2.10431 9.38885,4.00964 14.25634,5.59198 5.80793,2.27966 11.77924,3.9364 17.80402,5.55078 5.90802,1.59467 11.83118,3.12823 17.88971,4.05261 4.82825,0.82874 9.63055,1.58075 14.22595,3.3902 4.54822,1.51807 9.14825,3.26361 13.9964,3.30719 11.35046,-2.4104 -5.40344,4.6001 7.95441,-4.82678 6.62487,-6.09076 13.44513,-11.97406 20.11889,-18.00855 5.38843,-4.74896 11.40106,-8.89404 15.89923,-14.55176 3.6803,-5.44281 6.81415,-11.01351 8.3631,-17.48522 1.35242,-6.03049 0.63367,-11.99826 1.88434,-18.03174 2.62848,-5.95291 8.1543,-9.66327 14.8255,-10.56995 l -6.96631,5.06134 z"
- id="path11351"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 716.77472,444.32047 c -2.74524,-1.18049 -5.34046,-2.85627 -7.95667,-4.36408 -3.6983,-1.95227 -5.92669,-4.51767 -7.51855,-8.25015 -1.04474,-4.1771 -0.3512,-8.24186 0.58655,-12.34024 1.30743,-5.68326 3.10254,-11.13327 6.06372,-16.17542 5.48755,-8.78912 12.39166,-14.29382 22.27118,-17.92306 7.25317,-1.53321 14.53741,-2.71668 21.9533,-2.98859 8.27662,-0.29333 16.58252,-0.50571 24.85944,-0.27756 9.18701,1.02603 17.41272,4.63645 25.80603,8.19288 7.29083,3.33541 15.05951,5.13446 22.92639,6.42556 6.82868,0.8187 13.40656,2.24759 19.90705,4.42881 5.0343,1.46127 8.36749,3.93621 11.43695,8.01782 2.14795,3.4975 2.65783,7.54336 3.62548,11.44867 1.39924,4.76339 2.85718,9.51724 3.95154,14.35855 0.44055,4.79327 1.08509,9.56619 1.48908,14.3648 0.64776,5.26233 0.24664,9.15696 -3.27326,13.09263 -5.87829,4.8518 -9.55285,5.31613 -16.1228,5.806 l 6.40735,-4.71598 c 6.91675,-0.61264 10.45703,-2.3479 2.08966,1.97019 3.92401,-3.59931 4.28839,-7.13263 3.66638,-12.40891 -0.41443,-4.7763 -1.03357,-9.52188 -1.41534,-14.30035 -1.00293,-4.82715 -2.53131,-9.51996 -3.99457,-14.2266 -0.96606,-3.80639 -1.46289,-7.77084 -3.51013,-11.20733 -2.97003,-3.88794 -6.14386,-6.36188 -11.02002,-7.69159 -6.43036,-2.18115 -12.9483,-3.73129 -19.73505,-4.38947 -7.8717,-1.35073 -15.49908,-3.4855 -22.83703,-6.70285 -8.21656,-3.78683 -16.71277,-7.0541 -25.80976,-7.85965 -8.18152,-0.17154 -16.36694,-0.0692 -24.54809,0.14838 -7.48664,0.49118 -14.88068,1.69882 -22.17945,3.41531 -3.07342,1.27192 -8.53924,4.41309 2.41028,-2.01947 4.71613,-2.77057 -16.74682,11.93351 -11.27716,11.91989 -2.88525,4.91773 -4.54254,10.22941 -5.7138,15.78657 -1.0326,3.93035 -1.75348,7.87853 -0.77778,11.91619 1.66852,3.3935 3.5719,5.92258 7.14356,7.70642 2.55707,1.44437 5.16583,3.33265 8.06183,3.78129 l -6.96631,5.06134 z"
- id="path11353"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 855.93964,463.96115 c -6.13544,2.65604 -13.08118,3.24063 -19.75556,3.63318 -7.755,-0.0702 -15.73736,1.07422 -23.23102,-0.79166 -7.18896,-4.18206 -7.60864,-10.40832 -6.74444,-17.86947 1.03943,-5.65641 3.13586,-10.96286 1.95746,-16.61112 -4.85993,-3.85989 -11.7569,-5.23752 -17.66034,-7.2536 -13.20124,-4.00979 -25.33423,-10.92459 -38.67542,-14.49515 -0.49701,-0.10415 -0.99408,-0.20831 -1.49109,-0.31247 l 5.04706,-3.49527 c 0.48492,0.1218 0.96985,0.24363 1.45477,0.36542 13.073,4.11795 24.98096,11.39987 38.3313,14.76361 6.26502,1.82953 13.68238,2.92908 18.59839,7.27729 1.15058,5.8247 -0.85296,11.23819 -2.03558,17.05346 -0.8888,7.09088 -0.71326,13.32495 6.33935,17.15262 7.50483,1.56253 15.35224,0.72315 23.03461,0.59897 6.9129,-0.4036 13.86121,-1.18597 20.24878,-3.95242 l -5.41827,3.93661 z"
- id="path11363"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 752.19727,408.24936 c -1.73737,8.18359 -8.32038,12.58902 -14.54389,17.39252 -4.81848,3.92917 -7.11493,9.70355 -9.65765,15.19931 l -4.67804,2.14114 c 2.54645,-5.56906 4.80877,-11.4447 9.6474,-15.45862 5.95025,-4.58804 12.73175,-8.71814 14.11731,-16.66818 l 5.11487,-2.60617 z"
- id="path11367"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 787.1687,438.53018 c 3.6322,4.84915 6.31464,10.64475 8.72308,16.24381 3.56531,7.40884 2.883,14.42773 0.23438,21.93112 -4.69873,12.36563 -13.44672,19.16922 -24.59412,25.48611 -8.09271,3.70426 -16.85052,5.85703 -25.63995,3.94471 -9.28955,-2.331 -15.34741,-7.16901 -19.04511,-15.90915 -2.7561,-7.68491 -2.51458,-15.27088 0.0538,-22.90985 3.00787,-7.92432 8.61371,-13.94028 15.12231,-19.1363 7.64533,-6.06466 16.26239,-8.56918 25.6084,-10.02621 6.28052,-0.6474 12.24292,-0.70603 18.31232,1.00049 l -5.84803,4.01092 c -5.99603,-1.57412 -12.03161,-1.45279 -18.19226,-0.73782 -11.68548,1.96753 -12.44586,2.63821 -13.49658,3.21054 -6.50513,5.07248 -12.14868,10.9523 -15.23859,18.75992 -2.59606,7.43973 -2.8562,14.94992 -0.34265,22.50253 3.50739,8.46588 9.75562,13.19913 18.70398,15.33002 8.71289,1.51333 17.32855,-0.40711 25.25031,-4.26273 -6.8227,4.06354 11.72412,-9.5964 13.09656,-18.37079 2.65588,-7.33771 3.30474,-14.14193 -0.0441,-21.45248 -2.13837,-5.29303 -4.8338,-11.31195 -8.85608,-15.11588 l 6.19232,-4.49896 z"
- id="path11373"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 766.19159,455.25497 c 4.04785,-0.3648 6.44226,0.52292 9.42865,3.1998 3.36023,4.23554 4.32593,9.21769 3.85602,14.47562 -1.5531,7.07931 -8.49652,9.67712 -14.862,11.39166 -5.96827,2.21576 -9.87879,0.73648 -14.75867,-2.97067 -5.0993,-5.3804 -5.12592,-11.6341 -2.45465,-18.14963 4.94977,-5.88614 11.61121,-7.37241 18.77258,-7.74719 0.6626,0.10678 1.29645,-0.11121 1.9447,-0.16681 l -4.37567,3.25894 c -0.19543,0.0116 -1.78589,0.18268 -1.94659,0.11569 -9.63977,0.57517 -7.9198,0.65006 -9.49658,2.36533 -2.87812,6.194 -2.91693,12.32434 2.04571,17.53067 4.85126,3.5929 8.77204,4.60934 14.57642,2.44076 9.3938,-2.57663 0.13885,2.96506 5.69952,-5.69619 0.56378,-5.09406 -0.35565,-9.90802 -3.60992,-14.02762 -3.0149,-2.5477 -5.50775,-3.13733 -9.46375,-2.64612 l 4.64423,-3.37424 z"
- id="path11375"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 785.43897,464.85889 c -2.74958,0.14709 -0.39484,-2.3003 -1.88099,4.29635 3.78119,4.81473 4.49384,2.39289 4.82099,2.28858 2.53143,-3.83078 1.87359,-6.69223 -2.22522,-7.93552 l 3.1148,-2.28101 c 4.22046,1.6424 5.1048,4.59726 2.52344,8.76004 -3.10614,3.49875 -10.33453,7.69318 -11.59759,0.83838 0.96264,-4.55142 3.17322,-8.51767 8.52856,-8.35278 l -3.28399,2.38596 z"
- id="path16779"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 784.62225,482.43487 c 0.30762,-2.88009 -0.62604,-3.86322 -3.13525,-4.86337 -1.59888,-0.0577 -2.08569,-1.09729 -1.60376,5.14334 1.22241,0.92423 1.99603,2.33448 3.11676,3.37964 0.84228,-1.03821 0.82544,-2.2619 1.06122,-3.47522 l 3.50067,-1.76083 c -0.23322,1.27444 -0.22888,2.56631 -1.02503,3.68585 -4.30145,3.28619 -6.42944,5.17377 -10.02056,0.042 -0.45753,-5.17474 2.17065,-9.057 8.14477,-9.23648 2.52698,1.16062 3.67822,2.25363 3.57794,5.24222 l -3.61676,1.84283 z"
- id="path16789"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 770.45416,486.30148 c -5.651,0.0157 0.18238,-3.28363 -2.56872,0.76413 -1.05487,3.76096 1.13122,5.80143 4.11456,7.62992 4.66559,0.15182 0.51428,3.25988 1.73584,-2.54865 0.3689,-3.33231 -1.5296,-3.73959 -4.17261,-4.90686 l 3.19257,-2.14554 c 2.8291,1.19516 4.79248,1.83103 4.43207,5.34745 -0.74634,4.14251 -3.80554,8.25314 -8.3996,6.34561 -3.14197,-1.97235 -5.33813,-4.15512 -4.38,-8.13559 1.966,-3.34003 5.393,-5.83988 9.32989,-4.73642 l -3.284,2.38595 z"
- id="path16791"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 747.54633,487.55505 c -3.55176,-1.22485 -8.95526,1.3736 -4.68061,-0.65063 -2.06616,3.68698 0.42139,6.01385 3.72473,7.57846 3.71747,0.10657 1.34894,3.25024 3.6413,-3.3884 0.25873,-3.42401 -0.74957,-5.12381 -3.68188,-6.46621 l 3.19097,-2.1825 c 3.01209,1.54117 4.09375,3.39307 3.93738,6.97699 -1.37146,4.36874 -5.1272,8.79425 -10.25396,7.18585 -3.48829,-1.74231 -5.98621,-4.25671 -4.10267,-8.23214 3.91291,-2.50968 7.08094,-4.49131 11.67841,-2.93784 l -3.45367,2.11642 z"
- id="path16793"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 736.70862,475.43326 c -5.04804,-0.72693 -0.33484,-3.72217 -1.26532,0.66122 0.57312,4.08228 2.94043,5.99524 6.66772,7.15683 2.49561,0.16757 3.30005,-0.32867 0.6803,-5.46783 -1.81299,-2.00409 -4.18005,-2.38373 -6.72442,-2.72531 l 3.12872,-2.22287 c 2.63617,0.40194 5.03546,0.89453 6.87305,3.013 2.87194,5.53091 -1.02259,9.02279 -7.06867,9.57461 -3.8327,-1.30246 -6.30414,-3.3537 -6.9956,-7.55719 0.53314,-3.96994 4.42688,-6.82419 8.15789,-4.54889 l -3.45367,2.11643 z"
- id="path16795"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 738.29614,460.57318 c -0.26568,-0.92642 -1.1972,-0.0841 0.38153,4.49936 1.30768,1.05505 2.4137,2.40942 3.8993,3.19437 1.25445,-2.96332 2.07507,-5.18598 0.40051,-7.70667 -1.54913,-0.17212 -3.552,-0.96036 -4.52203,-1.27582 l 3.63159,-1.66333 c 0.0966,0.0114 3.13714,0.20273 4.17413,0.81092 1.74579,2.78485 1.20904,4.98404 -0.18274,8.23571 -4.45611,3.89872 -6.6394,5.88483 -10.71283,0.29139 -1.815,-4.98304 0.96393,-7.47434 6.21448,-8.77188 l -3.28394,2.38595 z"
- id="path16797"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 753.29614,456.28744 c -3.30194,-0.26168 -5.83648,-0.97778 -7.95373,-3.41494 0.9657,-4.15036 6.10125,-5.59681 9.7395,-7.25067 4.42651,-1.48359 6.9057,1.75463 8.06433,5.66122 -1.04034,5.1644 -7.42816,5.9823 -11.89868,6.02124 -0.0858,0.0143 -0.17145,0.0286 -0.25714,0.0429 l 3.2265,-2.35382 c 0.0851,0.011 0.17035,0.0219 0.25549,0.0329 7.04425,-0.0474 3.15235,1.63065 5.28113,-1.98886 -0.97565,-3.89517 -3.50781,-6.63952 -7.70325,-5.02194 -6.29559,2.92276 0.74646,-1.83783 -3.27173,2.94666 2.16907,2.14425 4.46534,3.11798 7.80152,2.93942 l -3.28394,2.38592 z"
- id="path16799"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 768.9057,453.99826 c -3.24481,-1.78958 -3.7959,-5.13693 -4.03998,-8.57696 1.90906,-5.81388 8.07495,-4.96866 11.84577,-1.81202 3.82153,5.2648 -0.40583,8.16205 -4.9375,10.775 l 2.98254,-2.40406 c -4.74451,2.80417 2.72461,-2.20037 -1.30182,-6.40078 -4.414,-3.5271 -6.01355,-4.01364 -5.20282,-1.86365 0.11914,3.38062 0.67346,6.56577 4.02338,8.06543 l -3.36957,2.21704 z"
- id="path16801"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 782.58185,451.28744 c -6.32477,1.27433 -2.66144,0.78379 -4.04187,1.6388 -2.0011,3.40179 -1.57062,4.97971 1.77325,6.66467 7.12409,-0.18228 2.13458,2.76212 4.45239,-1.61487 0.4441,-1.99322 -0.26391,-3.05703 -1.29626,-4.62344 l 3.26556,-1.979 c 1.09638,1.65054 1.94458,2.84994 1.47474,4.9606 -1.62348,4.57895 -6.50153,6.65039 -11.07697,5.40649 -3.6217,-1.67346 -4.05499,-3.7478 -2.13061,-7.2854 3.26782,-2.41491 6.48577,-5.3526 10.8637,-5.55377 l -3.28393,2.38593 z"
- id="path16803"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 762.58185,496.28744 c -1.10132,-2.15185 -1.66016,-4.38967 -3.07459,-6.45898 -6.71081,-2.5784 -4.49914,-1.77554 -5.60656,0.56711 -1.01899,3.77878 0.45007,6.078 3.85504,7.31525 4.07806,-1.51477 0.73193,2.89981 2.06921,-3.15918 0.0399,-0.34757 -0.2904,-0.46262 -0.43567,-0.69391 l 3.34961,-2.02799 c 0.009,0.0154 0.73059,0.74372 0.54798,0.90573 -0.48127,4.52902 -4.04615,6.90854 -8.69684,7.20041 -3.49603,-1.47415 -5.12225,-3.9277 -4.13373,-7.914 1.94208,-4.90268 7.90289,-7.38599 12.31061,-4.19043 1.44787,1.87668 2.07416,4.24353 3.09888,6.07007 l -3.28394,2.38592 z"
- id="path16805"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 851.97919,453.42282 c 6.22558,4.50324 26.13208,7.03354 16.71319,16.15418 -7.57196,5.49505 -14.53625,14.45755 -4.32812,19.2254 3.91528,0.36768 -5.22009,-5.04282 -13.41205,-2.47162 -1.48767,-4.19839 -1.93237,13.64249 7.48364,18.93451 -0.69116,0.75326 -20.00586,2.60873 -11.40588,-2.25967 -5.46838,4.43887 -5.13898,8.30419 -2.86774,14.08489 l -6.61883,3.88874 c -2.92939,-5.90265 -2.58966,-10.46079 2.46991,-15.26895 9.27099,-5.36078 28.71185,-7.37204 11.95904,3.88818 -12.68811,-8.72519 -9.52661,-22.39279 4.8385,-27.83295 13.82318,-3.87542 18.33325,7.53238 1.37921,11.48252 -11.33654,-4.43113 -4.02887,-15.62036 3.66523,-20.90854 8.88073,-8.62949 -9.77033,-11.01151 -16.44404,-14.14481 l 6.56794,-4.77188 z"
- id="path18389"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer2"
- display="none"
- style="display:none">
- <path
- d="m -273.45477,-64.200333 c -3.14762,-0.965004 -10.00235,-3.495804 -14.65732,-5.376694 -4.3461,-1.756111 -8.25317,-7.362915 -12.5018,-9.711716 -6.68253,-3.694328 -13.99527,-6.814812 -21.55487,-7.75975 -0.72708,-0.09088 -1.43698,-0.287384 -2.15549,-0.431091 -3.84555,-0.769119 -9.32056,-3.412933 -12.27197,-5.604263 -3.68979,-2.73951 -6.25058,-5.100006 -7.12741,-9.484143 -1.4592,-7.29597 2.28939,-14.98388 0.43112,-22.41705 -1.15582,-4.62318 -0.43112,-11.66512 -0.43112,-16.3817 0,-4.13857 -0.90049,-10.15677 -2.58658,-13.56318 -2.22797,-4.50119 -1.29328,-11.98652 -1.29328,-17.0447 0,-4.71463 -0.86221,-9.45981 -0.86221,-14.22623 0,-4.18875 -0.0226,-7.87598 1.7244,-11.40552 1.79059,-3.61757 5.03219,-5.52925 8.40317,-7.13167 3.78683,-1.80008 8.33316,-5.24408 12.28949,-6.03535 4.21823,-0.84364 7.2258,-2.15549 11.63964,-2.15549 2.53788,0 3.38794,2.64742 6.03534,0 1.67438,-1.67438 3.44879,-1.23878 3.44879,-5.17316 0,-2.40097 4.7436,-1.82121 7.08582,-1.29328 0.52783,0.11895 -2.63004,6.74942 -2.77484,7.32862 -0.74429,2.97715 -0.93707,3.37659 2.77484,4.74209 2.46674,0.90744 6.05255,2.13555 8.00259,3.01767 1.75415,0.79354 0.81787,4.61267 0,6.26509 -3.79104,7.65902 2.15146,2.61364 6.46643,1.74189 3.5499,-0.71717 4.40827,-1.34887 3.4488,-5.22567 -0.76941,-3.10887 0.39529,-4.20063 0.86218,-5.36789 1.34561,-3.36398 1.06393,-3.49228 3.87988,-5.60426 2.74478,-2.05858 8.35163,-0.20275 10.34635,1.2933 2.55453,1.91591 6.10705,4.81476 8.79925,6.03535 2.92001,1.32386 2.61286,6.19889 2.61286,8.86918 0,4.60664 1.30641,8.56216 1.30641,13.11679 0,3.64929 0.55057,5.30941 3.48381,6.03537 6.9377,1.71698 14.07313,8.11293 20.00981,12.07071 2.29817,1.53212 -3.44876,4.77229 -3.44876,7.53431 0,5.53394 0.5916,12.28699 1.29329,17.90042 0.72956,5.83655 0.86219,11.70137 0.86219,17.67499 0,4.25889 0.0563,8.25622 0.86219,12.32673 1.14641,5.79019 1.82654,9.9086 0.8622,15.694595 -0.60701,3.642075 -3.72564,7.215286 -5.60427,10.346313 -2.27903,3.798416 -6.48176,5.873367 -9.91523,8.796997 -3.53769,3.01239 -8.60985,6.198959 -12.75998,8.27401 -4.22727,2.113655 -8.52704,2.759468 -13.10587,2.759468 -2.74795,0 -1.45843,0.174271 -3.87985,-0.431084 z"
- id="path5693"
- inkscape:connector-curvature="0"
- style="fill:#eee1cf;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -272.1615,-65.924721 c -2.73968,0 -9.1535,-3.364822 -12.5018,-5.394196 -7.08444,-4.293831 -12.02811,-10.653526 -20.26157,-12.711891 -5.78979,-1.447449 -11.90017,-3.794594 -17.24389,-6.466469 -4.71213,-2.356056 -9.71366,-1.546646 -14.42746,-3.87986 -5.07862,-2.513786 -6.11969,-7.395973 -7.9896,-12.070723 -2.12649,-5.3162 0,-14.41943 0,-20.26157 0,-5.87524 -1.72439,-11.32754 -1.72439,-17.24389 0,-6.54782 -0.27976,-12.46529 -1.29328,-18.96826 -0.81772,-5.24643 -1.03671,-12.06048 0,-17.24392 0.74585,-3.72931 -0.43112,-7.88314 -0.43112,-11.63961 0,-5.81215 2.8364,-6.35589 8.39005,-7.10769 2.15612,-0.29187 4.19067,3.4838 7.12945,3.4838 3.22064,0 4.17942,-2.97267 9.05307,-1.7419 0.57477,0.14516 1.14957,0.29031 1.72437,0.43549 3.3677,0.85046 6.4899,1.91848 9.91525,2.77481 4.0524,1.0131 8.24167,3.64155 12.07071,5.17315 4.21252,1.68503 5.74359,1.50576 7.57382,6.03537 1.54111,3.81411 0.87094,10.6168 0.87094,14.65732 0,5.22393 -0.40729,10.72123 0.43546,15.77768 0.56863,3.41171 5.77023,3.46886 7.50146,5.77718 2.08298,2.7773 5.29218,4.60849 6.89755,8.62195 1.72712,4.31778 0.18406,9.60519 1.7244,14.2262 2.11472,6.34412 1.2933,14.01105 1.2933,20.69267 0,7.886668 1.7244,15.694293 1.7244,23.710338 0,0.862206 0,1.724396 0,2.586578 0,4.519661 -0.41315,10.762466 -3.0177,12.932915"
- id="path6453"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7973);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -229.05177,-91.79055 c 0.0571,1.975037 -1.70552,6.447594 -3.87987,8.621933 -2.07245,2.072456 -5.61832,2.805634 -8.19086,3.448784 -3.60309,0.900772 -7.18469,4.13028 -10.55632,6.659012 -3.93368,2.950264 -6.20591,4.886314 -10.99852,6.096634 -2.11224,0.533417 -5.84921,-0.06186 -7.32867,-0.435486 -2.25714,-0.570007 -1.68012,-9.910614 -1.29328,-11.45797 1.02741,-4.109711 1.29328,-9.072479 1.29328,-13.364014 0,-4.386147 -0.15692,-8.701373 -0.86218,-12.932913 -0.84403,-5.06405 -2.15549,-10.2978 -2.15549,-15.5195 0,-5.35208 0.43109,-10.68477 0.43109,-15.95059 0,-3.43198 0.4032,-5.73296 -1.7244,-7.32866 -2.27462,-1.70597 -4.3392,-3.0318 -6.89755,-4.31098 -2.70154,-1.35077 -5.93149,-3.3303 -7.32864,-4.92373 -1.94501,-2.21821 -1.35639,-4.93117 -2.32831,-8.00918 -1.06317,-3.36711 -0.87094,-8.08558 -0.87094,-11.63961 0,-2.73029 0,-5.46057 0,-8.19088 0,-3.09643 1.34561,-5.90611 3.19925,-7.75975 1.87283,-1.87281 4.95135,-2.62593 7.75976,-4.31095 3.71302,-2.22782 10.7027,-2.93116 14.65729,-4.0725 4.63837,-1.3387 8.629,-4.46606 12.70542,-6.09663 2.98974,-1.19591 2.17736,4.49214 2.17736,6.09663 0,2.95705 -0.43547,6.17458 -0.43547,9.24565 0,5.01163 -0.2942,6.1752 3.04833,9.48416 2.05765,2.03697 4.71869,2.26967 7.07692,3.44876 2.6224,1.31124 4.17854,1.93009 6.89753,3.0177 4.05234,1.62093 3.71421,5.30498 3.01769,9.48413 -0.52686,3.16106 0,6.96239 0,10.17342 0,3.14737 1.7244,6.0088 1.7244,9.22595 0,3.66617 1.34309,6.66568 2.15548,9.91526 1.10364,4.41456 0,10.72449 0,15.26123 0,4.6305 0.43109,9.09151 0.43109,13.62226 0,3.38295 0.11531,7.098034 -1.2933,9.915241 -0.64217,1.284355 -0.43109,3.321609 -0.43109,4.742065"
- id="path7221"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7970);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -326.91083,-187.92525 c 1.1163,1.01096 3.35559,5.23153 3.87989,7.32866 0.67218,2.68878 -0.9643,4.38754 -2.58661,5.60426 -2.54456,1.90845 -5.41935,-1.53945 -6.46646,-2.58657 -2.03019,-2.03019 -2.33484,-4.45661 -2.33484,-7.32866 0,-3.03647 1.26797,-3.72093 3.62814,-4.31098 2.80191,-0.70047 3.87988,1.36879 3.87988,3.87989"
- id="path9531"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -325.61755,-171.11244 c 2.52432,-0.63107 2.5076,8.61799 2.15551,10.52136 -0.75119,4.06084 -11.30603,-4.08475 -12.26324,-5.34819 -5.48935,-7.24557 9.03464,-4.63663 10.10773,-5.17317 0.89972,-0.44985 0,2.01179 0,3.01768"
- id="path9533"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -326.91083,-155.80087 c 0.31159,-0.15578 4.3822,5.21991 1.29328,7.53657 -3.42838,2.57131 -8.10813,0.0528 -10.10773,-2.58658 -4.35196,-5.74426 3.0972,-6.2564 6.65897,-6.2564 0.84014,0 1.43698,0.87094 2.15548,1.30641 z"
- id="path9535"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -329.9285,-143.95332 c 2.41321,0 5.18479,3.9032 6.46646,6.46646 1.72876,3.45756 -2.11291,4.31099 -4.311,4.31099 -4.24509,0 -6.70029,-4.09557 -7.51676,-7.32868 -0.80709,-3.19589 3.55851,-3.44877 5.3613,-3.44877 z"
- id="path9537"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -332.08402,-131.45151 c -0.41244,-1.63324 8.14664,4.8268 9.05307,6.03537 2.64841,3.53123 -4.20114,6.00791 -6.03537,6.46646 -4.90594,1.22649 -7.13983,-5.35289 -7.96539,-8.62194 -0.48346,-1.91445 5.5976,-6.47945 4.5166,-2.15549"
- id="path9539"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -333.37729,-118.51858 c -0.76053,-3.01151 6.15137,3.25002 6.89755,6.2651 0.83493,3.37363 -4.22506,4.64159 -6.46646,4.08123 -5.14963,-1.28741 -5.20746,-6.00761 -2.34359,-8.87144 0.56924,-0.56926 1.27499,-0.98326 1.9125,-1.47489 z"
- id="path9541"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -327.77304,-105.58566 c 2.20206,-2.20205 2.37775,6.021916 1.7244,7.328633 -1.25605,2.5121 -7.65067,-1.202477 -8.8057,-1.72438 -4.76865,-2.154703 3.04297,-5.173163 4.92584,-5.173163 2.62732,0 1.45227,-1.13431 4.31095,1.72439"
- id="path9543"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -306.64926,-175.42342 c 2.97601,0 2.58658,3.30851 2.58658,6.89757 0,3.21285 -4.9607,0.3749 -6.03537,-0.4311 -3.01053,-2.25791 -2.0643,-9.05304 1.72439,-9.05304 1.7305,0 1.35468,0.73803 1.7244,2.58657 z"
- id="path9545"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -305.35596,-160.15561 c 1.98938,0 1.49186,7.48163 0.86219,8.87364 -1.26621,2.79921 -7.63175,-2.89488 -8.19086,-3.64795 -2.85806,-3.84945 0.66409,-6.69838 4.31097,-6.69838 1.1193,0 2.01181,0.9818 3.0177,1.47269 z"
- id="path9547"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -306.64926,-147.83319 c 1.33987,0 3.44876,5.78899 3.44876,8.62195 0,3.2848 -4.50412,2.58658 -6.89755,2.58658 -6.01801,0 3.04452,-10.53473 3.44879,-11.20853 z"
- id="path9549"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -305.35596,-130.58929 c -2.74539,0 0.15741,5.64182 -0.86221,8.19083 -0.67557,1.6889 -6.33426,-1.16108 -6.46646,-1.29329 -4.09482,-4.09482 1.36816,-8.19085 5.17316,-8.19085 4.39166,0 1.61898,3.04404 0,3.44878"
- id="path9551"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -306.21817,-115.73727 c 3.01444,0 2.108,4.45068 1.2933,6.09664 -1.12771,2.2783 -7.77182,-1.96878 -8.62194,-2.61285 -3.6211,-2.74339 3.72702,-5.36899 5.17315,-5.66116 1.00104,-0.20224 1.43698,1.45158 2.15549,2.17737"
- id="path9553"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -309.66693,-105.58566 c 2.39386,2.41817 3.87985,1.85015 3.87985,6.466443 0,2.735764 -4.20437,1.724396 -6.46646,1.724396 -5.11002,0 0.75797,-5.470429 3.0177,-6.035359"
- id="path9555"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -307.08035,-93.946037 c 3.14483,-0.786209 2.37033,3.987984 0,5.173157 -2.47715,1.238564 -5.21756,0.386734 -6.46646,-0.86219 -2.12928,-2.129272 6.26648,-4.277641 6.46646,-4.310966 z"
- id="path9557"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -282.93893,-185.33865 c -1.83179,-1.83182 5.92794,-1.40071 7.75976,0.43109 2.44846,2.44847 -2.89914,4.49688 -3.87988,4.74206 -2.58295,0.64575 -5.60425,2.36591 -5.60425,-1.29329 0,-1.88916 0.53876,-1.50865 1.72437,-3.87986 z"
- id="path9559"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -264.40173,-184.47646 c 0,-3.34549 -7.0744,-4.39572 1.72439,-7.32865 4.2287,-1.40957 5.37915,4.10115 2.15546,6.03537 -1.81421,1.08851 -0.97369,0.38946 -2.15546,0.86218"
- id="path9561"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -257.93527,-179.73437 c -2.00147,1.20086 -2.99152,4.01921 -7.32865,4.74205 -2.80157,0.46694 -2.63439,-6.46647 2.58658,-6.46647 2.73816,0 3.04801,-0.53433 4.74207,1.72442 z"
- id="path9563"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -274.74808,-174.56122 c 3.08844,0 -0.67758,4.69593 -2.58657,5.17317 -4.55738,1.13935 -5.65274,1.62742 -7.75977,-2.58657 -1.65299,-3.30602 6.23547,-4.74209 8.19086,-4.74209 0.87408,0 0.28741,1.72438 0.43109,2.58658"
- id="path9565"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -255.59808,-145.6777 c 3.39186,0 1.78789,7.32865 -3.6305,7.32865 -6.13507,0 0.41263,-6.60089 3.6305,-7.32865 z"
- id="path9567"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -235.08713,-154.92992 c -2.85513,0 0.0592,6.16054 -1.72439,8.39002 -3.15108,3.93886 -5.60427,-1.87074 -5.60427,-4.31097 0,-3.57822 5.14061,-3.52649 7.32866,-4.07905 z"
- id="path9569"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -256.46906,-134.46918 c 0,-2.17495 1.40351,4.35772 0.87098,6.46646 -0.86328,3.41845 -5.20633,1.44184 -7.07926,-0.4311 -3.18625,-3.18624 1.15369,-5.57634 3.44876,-6.03536 0.90198,-0.1804 1.83969,0 2.75952,0 z"
- id="path9571"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -239.82919,-142.22893 c 3.62269,-0.72455 4.88614,2.8725 3.87986,6.89755 -1.20297,4.81191 -7.54615,0.63685 -8.19084,-2.58657 -1.00325,-5.01622 9.64942,-3.24329 4.31098,-4.31098 z"
- id="path9573"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -260.52188,-121.96736 c -0.47574,-2.37877 6.36751,2.71724 5.35927,4.92367 -0.91796,2.00885 -5.02999,1.30642 -7.08364,1.30642 -2.24527,0 0.21018,-6.98719 3.44876,-5.36789"
- id="path9575"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -239.82919,-129.72711 c -1.52755,0 3.1492,1.5198 3.44877,3.01768 0.84755,4.23782 -2.11837,4.64357 -5.17315,3.87988 -3.3888,-0.8472 0.57554,-6.32314 1.72438,-6.89756 z"
- id="path9577"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -256.46906,-109.64063 c -1.42746,-0.35688 3.04834,1.72133 3.04834,3.19277 0,0.7185 0,1.43699 0,2.15548 0,3.64516 -7.10116,1.5538 -7.10116,-2.58658 0,-2.97004 1.09085,-2.76167 4.05282,-2.76167 z"
- id="path9579"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -237.24261,-119.81188 c 0,-0.55047 6.38076,7.28251 -3.01769,8.86483 -5.1039,0.85929 -0.11626,-7.82017 3.01769,-8.86483 z"
- id="path9581"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -260.09076,-98.257027 c -2.05426,0.345863 4.97659,-1.229324 6.23457,0.431114 3.24035,4.277046 -4.7635,4.355415 -6.66569,3.879875 -6.98599,-1.746513 1.42065,-4.805756 3.0177,-5.604263"
- id="path9583"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -241.55357,-102.56799 c -2.12342,-0.53085 5.60609,-4.73283 6.03534,-2.58658 0.75546,3.77733 -7.75467,4.33126 -8.62194,0.86219"
- id="path9585"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -254.72713,-89.203972 c 0.50219,2.510979 -1.11276,5.604248 -5.36363,5.604248 -4.14249,0 -0.491,-6.667023 2.15549,-7.328644 1.36755,-0.341896 2.20131,0.984314 3.20814,1.724396 z"
- id="path9587"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -235.51823,-97.394821 c 1.65161,-0.412895 -0.64766,5.352959 -4.74207,6.035355 -1.31785,0.21965 -2.0363,-7.391037 4.74207,-6.035355 z"
- id="path9589"
- inkscape:connector-curvature="0"
- style="fill:#91c0e3;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -326.62265,-184.56149 c 1.09711,0.9611 3.29785,4.97358 3.81314,6.96733 0.66061,2.55621 -0.94769,4.17123 -2.54211,5.32795 -2.5008,1.81437 -5.32611,-1.46356 -6.35523,-2.45904 -1.99527,-1.9301 -2.29471,-4.23688 -2.29471,-6.96735 0,-2.88676 1.24619,-3.53747 3.56577,-4.09843 2.75369,-0.66592 3.81314,1.3013 3.81314,3.68858"
- id="path10409"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7933);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -325.35162,-168.57761 c 2.48089,-0.59996 2.46444,8.1931 2.1184,10.00263 -0.73825,3.86063 -11.11154,-3.88335 -12.0523,-5.0845 -5.39493,-6.88836 8.87927,-4.40804 9.9339,-4.91813 0.88424,-0.42767 0,1.9126 0,2.8689"
- id="path10411"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7930);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -326.62265,-154.02093 c 0.30621,-0.14811 4.30682,4.96253 1.27103,7.165 -3.36942,2.44454 -7.96866,0.0502 -9.9339,-2.45906 -4.27707,-5.46106 3.04394,-5.94796 6.54446,-5.94796 0.82569,0 1.41226,0.82801 2.11841,1.24202 z"
- id="path10413"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7927);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -329.58844,-142.75751 c 2.3717,0 5.09564,3.71076 6.35523,6.14766 1.69906,3.2871 -2.07657,4.09844 -4.23682,4.09844 -4.17209,0 -6.58505,-3.89365 -7.38748,-6.96733 -0.79322,-3.03835 3.49728,-3.27877 5.26907,-3.27877 z"
- id="path10415"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7924);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -331.70685,-130.87204 c -0.40536,-1.55272 8.0065,4.58881 8.89734,5.7378 2.60285,3.35713 -4.12887,5.7117 -5.93155,6.14764 -4.82159,1.16602 -7.01703,-5.08898 -7.8284,-8.19685 -0.47513,-1.82007 5.50135,-6.15999 4.43894,-2.04922"
- id="path10417"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7921);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -332.97791,-118.57676 c -0.74743,-2.86302 6.04557,3.0898 6.77894,5.95622 0.82055,3.20731 -4.15238,4.41275 -6.35523,3.88002 -5.06107,-1.22394 -5.11789,-5.71142 -2.30331,-8.43405 0.55944,-0.5412 1.25308,-0.93479 1.8796,-1.40219 z"
- id="path10419"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7918);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -327.47003,-106.28146 c 2.16418,-2.09348 2.33685,5.72504 1.69473,6.967327 -1.23444,2.388229 -7.51907,-1.143187 -8.65424,-1.639367 -4.68661,-2.04847 2.99064,-4.91811 4.8411,-4.91811 2.58215,0 1.42731,-1.0784 4.23682,1.63937"
- id="path10421"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7915);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -306.70959,-172.67604 c 2.92483,0 2.54208,3.14539 2.54208,6.55749 0,3.05446 -4.87537,0.35642 -5.93155,-0.40985 -2.95877,-2.14657 -2.02878,-8.6067 1.69473,-8.6067 1.70072,0 1.33139,0.70166 1.69474,2.45906 z"
- id="path10423"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7912);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -305.43857,-158.16098 c 1.9552,0 1.46622,7.11278 0.84738,8.43616 -1.24444,2.66121 -7.50045,-2.75218 -8.04998,-3.46811 -2.8089,-3.65965 0.65268,-6.36814 4.23684,-6.36814 1.10004,0 1.97718,0.9334 2.96576,1.40009 z"
- id="path10425"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7909);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -306.70959,-146.44607 c 1.3168,0 3.38943,5.50357 3.38943,8.19685 0,3.12288 -4.42664,2.45904 -6.7789,2.45904 -5.91449,0 2.99213,-10.01533 3.38946,-10.65589 z"
- id="path10427"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7906);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -305.43857,-130.05235 c -2.69812,0 0.1547,5.36367 -0.84735,7.78701 -0.66394,1.60562 -6.22531,-1.10384 -6.35525,-1.22953 -4.02439,-3.89294 1.34463,-7.78702 5.08419,-7.78702 4.31614,0 1.59113,2.89395 0,3.27875"
- id="path10429"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7903);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -306.28592,-115.93257 c 2.96259,0 2.07175,4.23126 1.27103,5.79607 -1.10828,2.16598 -7.63812,-1.87172 -8.47363,-2.48404 -3.55881,-2.60813 3.66293,-5.10429 5.08419,-5.38205 0.98383,-0.19226 1.41227,1.38003 2.11841,2.07002"
- id="path10431"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7900);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -309.67538,-106.28146 c 2.35269,2.29895 3.81314,1.75893 3.81314,6.14765 0,2.600866 -4.13205,1.639357 -6.35523,1.639357 -5.02216,0 0.7449,-5.200717 2.96579,-5.737787"
- id="path10433"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7897);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -307.1333,-95.215714 c 3.09076,-0.747429 2.32956,3.791397 0,4.918137 -2.43448,1.17749 -5.12778,0.367661 -6.35523,-0.819687 -2.09265,-2.0243 6.15869,-4.06675 6.35523,-4.09845 z"
- id="path10435"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7894);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -283.4071,-182.10245 c -1.8003,-1.74148 5.82599,-1.33165 7.62628,0.40985 2.40634,2.32776 -2.84928,4.2752 -3.81314,4.50829 -2.53852,0.61389 -5.50785,2.24924 -5.50785,-1.22952 0,-1.79602 0.52948,-1.43428 1.69471,-3.68862 z"
- id="path10437"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7891);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -265.18875,-181.28274 c 0,-3.18056 -6.95273,-4.17901 1.69473,-6.96735 4.15595,-1.34006 5.28665,3.89896 2.11841,5.73781 -1.78302,1.03485 -0.95697,0.37027 -2.11841,0.81968"
- id="path10439"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7888);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -258.83353,-176.77447 c -1.96704,1.14167 -2.94003,3.82106 -7.2026,4.50827 -2.75339,0.4439 -2.58908,-6.14763 2.54211,-6.14763 2.69104,0 2.99558,-0.508 4.66049,1.63936 z"
- id="path10441"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7885);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -275.35715,-171.85635 c 3.03534,0 -0.66592,4.4644 -2.54208,4.91812 -4.47897,1.08317 -5.55551,1.54718 -7.62628,-2.45906 -1.62458,-3.14302 6.1282,-4.50827 8.04995,-4.50827 0.85907,0 0.28248,1.63936 0.42371,2.45906"
- id="path10443"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7882);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -256.53653,-144.39687 c 3.33351,0 1.75713,6.96734 -3.56805,6.96734 -6.02955,0 0.40551,-6.27544 3.56805,-6.96734 z"
- id="path10445"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7879);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -236.37839,-153.19293 c -2.80601,0 0.0582,5.85681 -1.69471,7.97638 -3.09688,3.74466 -5.50788,-1.77852 -5.50788,-4.09844 0,-3.40181 5.05219,-3.35261 7.20259,-3.87794 z"
- id="path10447"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7876);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -257.39252,-133.74094 c 0,-2.06772 1.37937,4.14286 0.85599,6.14764 -0.84845,3.24991 -5.11679,1.37075 -6.95749,-0.40984 -3.13144,-3.02915 1.13385,-5.30142 3.38944,-5.7378 0.88647,-0.1715 1.80804,0 2.71206,0 z"
- id="path10449"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7873);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -241.03889,-141.11812 c 3.56039,-0.68883 4.8021,2.73087 3.81314,6.5575 -1.18228,4.57466 -7.41635,0.60542 -8.04996,-2.45906 -0.98599,-4.76893 9.48344,-3.08339 4.23682,-4.09844 z"
- id="path10451"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7870);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -261.37561,-121.85549 c -0.46759,-2.2615 6.25795,2.58326 5.26706,4.68092 -0.90216,1.90981 -4.94348,1.242 -6.96179,1.242 -2.20667,0 0.20657,-6.64269 3.38943,-5.10325"
- id="path10453"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7867);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -241.03889,-129.23267 c -1.50127,0 3.09504,1.44488 3.38946,2.8689 0.83298,4.02889 -2.08192,4.41463 -5.08418,3.68859 -3.33051,-0.80544 0.56566,-6.01139 1.69471,-6.55749 z"
- id="path10455"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7864);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -257.39252,-110.1365 c -1.40292,-0.33929 2.9959,1.63647 2.9959,3.03535 0,0.68307 0,1.36615 0,2.04922 0,3.46544 -6.97899,1.47719 -6.97899,-2.45906 0,-2.82361 1.07205,-2.62551 3.98309,-2.62551 z"
- id="path10457"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7861);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -238.4968,-119.80628 c 0,-0.52334 6.27102,6.92346 -2.96577,8.42776 -5.01611,0.81693 -0.11426,-7.43462 2.96577,-8.42776 z"
- id="path10459"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7858);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -260.95193,-99.314133 c -2.01893,0.328796 4.89099,-1.168727 6.12731,0.409851 3.18464,4.06617 -4.68154,4.140678 -6.55099,3.688568 -6.86585,-1.660378 1.39618,-4.56881 2.96576,-5.327936"
- id="path10461"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7855);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -242.73361,-103.41255 c -2.08689,-0.50468 5.50969,-4.4995 5.93155,-2.45906 0.74246,3.59109 -7.6213,4.11772 -8.47365,0.81968"
- id="path10463"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7852);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -255.68056,-90.70742 c 0.49353,2.387184 -1.09361,5.32795 -5.27137,5.32795 -4.07126,0 -0.48258,-6.338326 2.1184,-6.967316 1.34406,-0.32505 2.16346,0.935783 3.15297,1.639366 z"
- id="path10465"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7849);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -236.80206,-98.494453 c 1.62318,-0.39254 -0.63652,5.08905 -4.66051,5.737801 -1.2952,0.208832 -2.00127,-7.026634 4.66051,-5.737801 z"
- id="path10467"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7846);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m -343.72363,-204.30695 c -2.82581,5.47423 -4.89551,11.10028 -4.95163,17.20849 0.52417,9.06487 0.24008,18.21871 1.20434,27.25129 1.31845,8.52136 2.9693,16.97305 3.60105,25.5811 0.29293,6.53776 0.47824,13.08341 0.55319,19.62767 0.009,4.9913 -0.53626,10.06424 1.04092,14.867106 2.47235,4.587852 6.88913,7.312927 11.53101,9.378441 5.57059,1.46843 11.15216,2.424385 16.63184,4.352592 5.69586,1.919159 10.7182,5.235847 15.96353,8.083183 3.48144,2.003334 6.99124,3.965355 10.61395,5.700684 3.03168,1.252617 5.84576,2.902397 8.63116,4.613167 2.57651,1.137123 5.24637,1.9161 8.0355,2.202065 3.85876,-0.01628 7.34213,-1.618301 10.93792,-2.789536 8.87273,-3.172516 8.20983,-3.351715 9.46469,-4.145195 4.31771,-2.68045 8.67548,-5.30352 12.61789,-8.518539 3.3944,-2.518051 5.4417,-5.742455 6.63697,-9.7118 0.71057,-3.811646 0.50068,-7.709122 0.78019,-11.560468 0.49086,-5.51099 0.49546,-11.04765 0.50669,-16.57558 0.14466,-5.33654 -0.0876,-10.65985 -0.75698,-15.95497 -0.93197,-4.50298 -1.25541,-9.07968 -1.76265,-13.63802 -0.78908,-5.76614 -0.27249,-11.57003 -0.11166,-17.35516 0.0597,-4.22543 -2.88062,-6.05151 -6.57359,-7.01999 -4.08323,-0.93478 -8.332,-1.42586 -12.32428,-2.59471 -3.04959,-2.59563 -3.43512,-6.76904 -3.61698,-10.57718 0.30545,-3.88753 0.39923,-7.78484 0.44077,-11.68257 0.28956,-4.01509 -0.57083,-6.67818 -4.09854,-8.58974 -3.36365,-1.31434 -6.69059,-2.70862 -9.99402,-4.16704 -1.36066,-0.58232 -2.74634,-1.09764 -4.13336,-1.61257 l 3.90374,-2.6261 c 1.40192,0.51936 2.80176,1.04374 4.19632,1.58282 3.37497,1.38342 6.76386,2.73271 10.13581,4.1194 3.66327,1.99877 4.55442,4.9653 4.30967,9.09574 -0.0266,3.89776 -0.11287,7.79039 -0.42077,11.678 0.1683,3.62511 0.34442,7.64441 3.34274,10.08188 4.00942,1.13625 8.20816,1.70739 12.32094,2.47329 3.8351,1.18391 7.00796,3.28195 6.65977,7.7469 -0.3385,5.7596 -0.52005,11.51993 0.12767,17.26933 0.4899,4.5452 1.02785,9.07987 1.86428,13.57707 0.54861,5.32987 0.7203,10.70668 0.67425,16.06426 -0.039,5.53141 -0.05,11.06885 -0.45968,16.58817 -0.2175,3.8489 0.11817,7.742943 -0.59546,11.551949 -1.11103,4.076035 -3.19735,7.413368 -6.59349,10.036835 -3.9016,3.3069 -8.34961,5.8647 -12.65654,8.601074 -5.57653,3.5662 -11.252,6.862335 -17.62404,8.77018 -3.64316,1.29734 -7.22168,2.804153 -11.16855,2.750103 -2.86414,-0.324329 -5.62091,-1.09153 -8.22504,-2.365017 -2.78232,-1.680824 -5.51086,-3.424294 -8.5538,-4.623154 -3.61322,-1.724266 -7.03653,-3.789467 -10.4896,-5.812813 -5.16479,-2.87632 -10.10894,-6.216713 -15.7453,-8.138443 -5.55639,-1.866486 -11.26223,-2.600357 -16.83239,-4.266014 -4.62528,-2.194473 -9.3602,-4.972366 -11.77249,-9.671432 -1.58185,-4.872318 -0.98999,-9.949988 -1.13199,-15.009898 0.0439,-6.60079 0.20953,-13.20667 -0.15299,-19.80256 -0.56872,-8.60707 -2.25012,-17.03931 -3.60348,-25.5486 -0.90808,-9.0629 -0.48596,-18.23543 -1.19168,-27.32126 -0.002,-5.88677 0.53793,-11.67752 2.90347,-17.12877 l 5.91071,-2.04566 z"
- id="path5591"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -301.43597,-216.74394 c -6.11139,2.15092 2.48477,-3.88553 -2.92508,2.22792 -2.58014,3.79048 -6.8693,8.10551 -11.05219,4.38463 -2.22589,-2.20444 -4.94189,-2.32157 -7.90518,-2.06591 -3.91983,0.73111 -7.42349,2.86145 -10.92426,4.70208 -3.43392,1.77807 -6.90316,3.48593 -10.45318,5.01646 -0.98093,0.47446 -2.04608,0.70161 -3.0788,1.01914 l 3.88736,-2.98235 c 1.00125,-0.32517 2.03241,-0.57002 2.98596,-1.03378 3.5448,-1.56279 7.02433,-3.27706 10.45765,-5.07273 3.52859,-1.82805 7.03817,-3.9581 11.01922,-4.55964 3.10703,-0.22116 5.98844,-0.0505 8.22949,2.39802 4.62348,3.27544 -3.01602,5.26367 2.39374,0.0863 3.0249,-3.51335 6.80283,-6.84547 11.6124,-7.20581 l -4.24713,3.08571 z"
- id="path5593"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -347.0582,-203.30319 c 2.57834,-0.21297 4.32764,0.25348 6.46152,1.68164 2.93469,1.94383 6.12753,3.60823 9.49863,4.63251 0.71402,1.50265 -1.39127,2.34399 -2.2952,-1.50338 1.29102,-3.33068 6.366,-4.64181 9.65738,-6.03663 0.96826,-0.15323 2.99225,-2.1201 11.69873,-6.14975 2.69662,-1.58627 5.0964,-3.64192 7.97446,-4.83666 l -2.04325,2.97071 c -2.86471,1.16194 -5.73425,2.30613 -8.42508,3.84568 -7.41012,3.40812 -1.41104,0.62388 -11.70013,6.15032 -6.39279,2.75271 -3.42947,0.74399 -4.18347,2.50226 0.81167,3.36709 0.83566,4.74379 -3.39704,4.92665 -3.36264,-1.11557 -6.59436,-2.76679 -9.51571,-4.7722 -2.16626,-1.30619 -4.02207,-1.6552 -6.56226,-1.35399 l 2.83142,-2.05716 z"
- id="path5595"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -329.9285,-201.91753 c 2.35169,-0.1352 5.23587,0.84139 7.41855,1.65644 3.14643,1.49125 6.32886,2.86596 9.62787,3.98095 4.14294,1.66314 8.44104,2.86156 12.65784,4.31356 3.88626,1.14358 7.96588,1.98495 10.77853,4.90808 0.76404,2.80488 -0.0277,5.84424 -0.36407,8.71704 -0.53498,3.06908 -0.53958,6.19257 -0.57111,9.29793 -0.0403,3.4788 -0.0914,6.95782 0.0839,10.42969 0.39051,2.90348 1.50275,5.64155 3.19364,8.00157 2.97787,1.73987 6.68628,1.59758 10.02185,2.44595 2.7576,0.88704 4.61926,2.65881 6.11133,5.04105 1.00552,2.68902 -0.11298,5.39613 -0.65049,8.08487 -0.33056,3.05517 -0.28949,6.13713 -0.32116,9.20643 -0.22382,4.38905 0.20437,8.71596 0.91174,13.04053 0.72546,5.8785 1.34168,11.77926 1.85165,17.682592 0.30735,3.903625 -0.33725,7.783134 -1.01336,11.613976 -0.60465,4.628578 -0.62601,9.301704 -0.26621,13.948135 0.20538,2.209084 0.72617,4.354134 1.26553,6.496357 l -2.9208,1.59515 c -0.51197,-2.188339 -0.98859,-4.380348 -1.16385,-6.630028 -0.29212,-4.658096 -0.41138,-9.347519 0.19025,-13.988876 0.64508,-3.832497 1.25262,-7.69265 1.12551,-11.593193 -0.34033,-5.906746 -1.11532,-11.778023 -1.85647,-17.644503 -0.75391,-4.31801 -1.21546,-8.65548 -1.01767,-13.04648 -0.0131,-3.07213 -0.0769,-6.15591 0.23935,-9.21605 0.49661,-2.62143 1.63821,-5.26329 0.71588,-7.8873 -1.43253,-2.3116 -3.2348,-3.99746 -5.90006,-4.85704 -3.39151,-0.87622 -7.21325,-0.62423 -10.15585,-2.57353 -1.68997,-2.46965 -2.94006,-5.18998 -3.24643,-8.21406 -0.20114,-3.47268 -0.13409,-6.95545 -0.14603,-10.43621 0.0358,-3.12129 0.0641,-6.2576 0.55823,-9.34726 0.35315,-2.78863 1.05881,-5.75911 0.43497,-8.49756 -2.72058,-2.8619 -6.72574,-3.64653 -10.46933,-4.82722 -4.25345,-1.41968 -8.59067,-2.57491 -12.76242,-4.2383 -3.30072,-1.12349 -6.4996,-2.47673 -9.64234,-3.98973 -2.23883,-0.75009 -4.43991,-1.21551 -6.82031,-0.95392 l 2.10138,-2.51904 z"
- id="path5597"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -274.33652,-138.71651 c 0.34751,-4.80024 3.6301,-6.07673 7.59806,-8.32551 3.458,-1.86556 6.98611,-3.51723 10.60684,-5.02356 3.76474,-1.44342 7.45004,-3.08692 11.06874,-4.86391 3.79877,-2.26849 1.27653,-0.0408 6.67032,-4.89436 0.71875,-0.64829 1.52228,-1.19333 2.29828,-1.76946 l 3.00439,-1.19871 c -0.76362,0.56633 -1.52456,1.14 -2.26997,1.72888 -3.68067,3.29468 -7.63071,6.08173 -12.19129,8.11855 -3.65127,1.80181 -7.3842,3.42598 -11.17056,4.92166 -3.60638,1.45428 -7.07648,3.15951 -10.51413,4.9842 -1.06476,1.33876 -1.45904,3.10479 -1.98233,4.73333 l -3.11835,1.58889 z"
- id="path5599"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -250.36926,-178.05321 c 1.32593,2.32015 0.22085,3.55889 -1.44377,5.28826 -3.10132,2.39396 -6.71331,4.03157 -10.32338,5.48787 -3.06855,1.53906 -6.422,2.23628 -9.61594,3.45794 -4.12738,1.77422 -8.39828,3.17568 -12.59314,4.77452 -3.33551,1.48095 -3.33551,1.29619 -5.41577,3.45168 -0.53827,1.68345 0.0915,2.73836 0.78699,4.18661 l -1.29331,1.41765 c -0.73724,-1.53074 -1.8269,-3.39261 -1.29327,-5.17317 1.84613,-4.83136 5.33072,-4.13097 9.72668,-5.84538 4.19928,-1.59365 8.46344,-3.01731 12.594,-4.78867 3.23929,-1.20006 6.60138,-1.99991 9.71362,-3.52743 5.8354,-2.42148 2.79947,-1.03226 4.61295,-2.20063 1.5287,-1.45544 3.21551,-3.23318 2.15149,-5.19275 l 2.39285,-1.3365 z"
- id="path5601"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -255.2961,-208.84586 c 1.70908,2.1352 1.01767,3.95839 -0.2106,6.08431 -2.972,3.20264 -6.98717,4.9136 -11.11882,6.06773 -4.20004,1.10933 -8.42499,2.097 -12.65399,3.08727 -5.97653,1.31036 -8.48673,3.34021 -9.04089,3.74554 -1.7013,1.61399 -2.05484,3.49411 -2.2652,5.69271 l -1.40637,1.41622 c 0.13555,-2.28323 -1.04071,-4.13473 0.63961,-5.87501 4.40219,-3.30737 9.17002,-5.75487 14.57883,-6.89632 4.25745,-0.99266 8.49908,-2.0235 12.72318,-3.14892 3.52847,-0.89254 3.95959,-1.75473 5.47274,-2.80637 1.26688,-1.88359 2.02503,-3.4874 0.45011,-5.31003 l 2.8314,-2.05713 z"
- id="path5603"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <g
- id="g7976"
- transform="matrix(0.748816,0,0,0.748816,-748.2099,-403.0788)">
- <path
- d="m 623.64496,260.84247 c 1.40362,1.24243 2.5058,5.05536 3.45422,7.1568 0.97852,2.16812 5.3219,3.30841 0,4.6524 -2.21264,0.55878 -1.33026,3.67984 -5.18133,4.65241 -3.82599,0.96621 -7.32685,1.96096 -11.16919,1.96096 -5.344,0 -1.99921,-5.1957 -0.58155,-6.61337 2.18897,-2.18896 3.00092,-4.72906 3.69086,-7.77926 0.43384,-1.9181 -7.74035,-1.72712 -8.9248,-1.72712 -3.78083,0 -7.52222,-3.612 -8.34638,-6.90847 -0.86065,-3.44263 2.15937,-6.67099 3.69397,-9.21128 2.29865,-3.80505 9.63782,-7.17676 13.57721,-8.63556 3.89386,-1.44195 7.18103,-3.59051 10.36267,-5.18135 3.89203,-1.946 7.67499,-2.49446 11.5141,-3.45424 4.25336,-1.06334 3.75672,1.78565 4.60565,5.18135 0.70172,2.8069 0.57569,5.65381 0.57569,8.63558 0,2.70177 -4.83075,6.70795 -6.33277,9.21127 -2.05066,3.41779 -7.74517,4.8667 -10.93835,8.05988 z"
- id="path7989"
- inkscape:connector-curvature="0"
- style="fill:#cedddf;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 637.46185,230.33011 c -0.87286,1.36403 -3.5169,7.03384 -5.18133,10.36268 -1.24725,2.49448 -4.84198,4.2663 -6.33277,5.75705 -2.13116,2.13118 -3.6759,3.85294 -6.33276,5.18136 -3.48614,1.74307 -5.14233,2.87852 -9.44788,2.87852 -3.33758,0 -6.86987,1.15141 -10.46789,1.15141 -3.85016,0 -5.55054,3.08176 -0.80957,4.02994 3.3266,0.66532 6.23486,1.05484 8.95129,1.72711 3.37238,0.83463 7.89044,0.54645 10.62268,0 4.52503,-0.90503 6.39502,-2.49344 9.21125,-4.60565 3.92981,-2.94733 7.4591,-3.9798 9.78698,-8.63557 1.78913,-3.57823 1.72712,-6.18012 1.72712,-10.36269 0,-2.54993 -3.23603,-13.5198 -1.72712,-7.48416 z"
- id="path8763"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient7966);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 641.2077,227.50401 c -0.20514,3.11214 -0.16436,6.24513 -0.14257,9.36505 0.13537,3.32801 0.0221,6.57791 -0.68634,9.82988 -1.07825,3.22473 -3.61091,5.36101 -6.0904,7.52785 -3.65082,3.44228 -7.59265,6.11214 -11.93927,8.60573 -3.7005,1.40924 -7.67151,2.03754 -11.58111,2.59744 -3.85389,0.15808 -7.57917,-0.88534 -11.11457,-2.33548 -3.70129,-1.58087 -5.93243,-2.94116 -4.77105,-7.19121 1.54187,-3.80258 4.46228,-6.75959 6.88513,-10.0052 1.63531,-2.48618 3.82306,-4.32573 6.23675,-6.0002 4.4798,-2.97561 9.43305,-4.9963 14.33509,-7.12983 3.63287,-1.5242 7.24823,-2.96431 11.04626,-4.02575 2.79791,-0.63125 5.64087,-1.04786 8.48071,-1.42157 l -3.31628,2.49283 c -2.80859,0.31557 -5.60638,0.73497 -8.36005,1.3812 -3.77679,1.07415 -7.35302,2.59553 -10.99292,4.05291 -2.20294,0.96331 -12.64672,6.22344 -6.11804,2.42936 0.41394,-0.24057 -1.62878,0.96435 -1.24121,0.72291 -2.41376,1.63321 -4.62732,3.425 -6.29236,5.85915 -2.41449,3.23974 -5.41607,6.13534 -6.96502,9.92572 -1.19519,4.03752 0.96154,5.12538 4.48126,6.63707 3.48956,1.42773 7.1723,2.47772 10.97912,2.17126 3.91285,-0.53283 7.89234,-1.1622 11.52979,-2.75625 -6.65955,3.89398 3.41791,-2.49213 5.02203,-4.50946 2.40796,-2.1083 4.82776,-4.19173 6.02564,-7.24375 0.78998,-3.21808 0.96564,-6.44159 0.80585,-9.75943 0.0213,-3.07113 0.0662,-6.15729 -0.14264,-9.21972 l 3.9262,-2.00051 z"
- id="path5609"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 638.88153,228.08557 c -0.56024,2.30174 -1.34894,4.53966 -2.31006,6.71144 -1.47082,2.94885 -3.4414,5.61936 -5.33081,8.31037 -1.57495,2.30609 -3.54651,4.17543 -5.74402,5.85771 -3.54547,2.56808 -7.53998,3.92747 -11.63037,5.26622 -2.89868,1.10681 -5.86474,1.96464 -8.84955,2.78807 -2.81061,0.52521 -5.61627,0.94604 -8.47503,1.07083 -0.43421,0.0165 -0.86866,0.0231 -1.30298,0.0346 l 3.34356,-2.44144 c 0.4314,-0.0121 0.86298,-0.017 1.29413,-0.0363 2.84186,-0.1766 5.65021,-0.59188 8.44196,-1.1508 2.98541,-0.80568 5.92749,-1.72079 8.82239,-2.81799 4.51757,-1.45751 8.29388,-3.23413 4.44268,-1.1668 2.23975,-1.66232 4.28754,-3.46832 5.89515,-5.78932 1.88574,-2.67222 3.83337,-5.33671 5.36084,-8.23561 0.9179,-2.05437 1.71209,-4.23404 2.1159,-6.40047 l 3.92621,-2.0005 z"
- id="path5611"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 617.43262,262.46097 c -0.0455,1.80975 -0.11255,3.60385 -0.3053,5.39801 -0.59833,2.78399 -2.17688,4.86221 -4.21716,6.79632 -2.20173,1.83911 -3.0232,1.60425 0.23559,1.34415 3.81958,-0.26254 7.64234,-0.0888 11.44818,-0.52615 5.51087,-2.46152 1.25635,2.19357 3.24048,-2.87644 -0.57702,-1.76513 -2.05493,-3.13171 -3.18255,-4.61014 -0.87836,-1.91571 -2.21442,-3.20373 -3.88544,-4.44638 l 3.23981,-1.99481 c 1.68762,1.2713 3.01464,2.66806 3.96057,4.56571 1.1983,1.51281 2.74963,2.8786 3.32239,4.7416 -0.75129,4.08725 -5.7392,5.90821 -9.64637,6.76856 -3.83148,0.35684 -7.6762,0.24988 -11.51233,0.55249 -3.4179,0.38827 -3.47259,-0.29636 -0.75939,-2.11179 2.05633,-1.84985 3.72241,-3.83429 4.30975,-6.58404 0.21112,-1.73389 0.34167,-3.47315 0.14886,-5.22873 l 3.60291,-1.78836 z"
- id="path5613"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 603.70624,255.19839 c 1.9862,-3.28465 2.14947,-7.22811 1.89679,-10.98945 -0.13251,-3.57163 -1.17646,-6.78716 -2.81152,-9.87566 2.81433,-3.12783 5.30792,-2.20651 9.05249,-1.68519 3.28265,0.39541 6.52716,0.27386 9.7752,-0.25136 1.22065,-0.23097 2.41974,-0.66107 3.59424,-1.01415 l -2.15655,1.71114 c -1.15253,0.28934 -2.32929,0.61578 -3.50183,0.86391 -3.26136,0.48973 -6.5185,0.56717 -9.8064,0.17663 -2.84424,-0.4012 -7.68787,-0.98814 -4.59747,-1.05926 1.49627,3.13888 2.52563,6.42463 2.63989,9.99696 0.21307,3.65665 0.16809,7.71124 -1.63098,10.87612 l -2.45386,1.25032 z"
- id="path5619"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 605.51605,231.71532 c 2.03815,1.31698 4.14905,2.58072 6.20343,3.89969 3.95783,2.42144 8.27918,4.14311 12.51142,6.01248 2.18359,1.13804 4.59686,1.65983 6.95745,2.21911 l -2.18054,1.46283 c -2.32831,-0.59414 -4.68457,-1.17663 -6.8313,-2.31538 -4.23626,-1.91171 -8.61981,-3.55627 -12.59009,-5.99995 -2.04052,-1.29319 -4.14483,-2.63817 -6.29846,-3.65998 l 2.22809,-1.6188 z"
- id="path5621"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- <path
- d="m -325.44177,-174.23441 c -2.29813,-0.0931 -4.45389,-0.72528 -6.46121,-1.82699 -3.01847,-1.76706 -4.91843,-4.49156 -5.92481,-7.78685 -0.81235,-4.19817 3.53171,-5.8259 7.00195,-6.33569 3.23786,0.076 4.45594,2.50941 5.97474,4.95649 1.65847,2.69261 1.88501,5.90515 1.55712,8.97551 -0.77313,1.9128 -2.9454,2.28188 -4.60409,3.05078 l 1.8649,-1.40579 c 1.7749,-1.79725 -1.64517,2.24735 0.63681,-0.64482 0.50018,-3.01767 0.22165,-6.16869 -1.43793,-8.82358 -1.47961,-2.41182 -2.64679,-4.75845 -5.84231,-4.74409 -3.20719,0.53656 -2.56623,-0.77059 -3.17423,3.89703 0.96973,3.25849 2.85172,5.91754 5.84866,7.62734 2.03373,1.089 4.23856,1.70971 6.56253,1.60605 l -2.00213,1.45462 z"
- id="path5625"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -326.31271,-160.29925 c -2.50232,-0.30755 -4.93213,-1.21557 -7.16745,-2.3669 -2.42948,-1.64742 -3.57441,-4.4821 -3.05457,-7.30231 2.49466,-2.28303 4.94742,-2.69374 8.13532,-2.44862 3.69409,0.86416 5.24377,3.20343 5.5689,6.80623 0.0553,1.43051 -0.41021,2.66265 -0.93148,3.93833 l -2.14939,0.94597 c 0.55857,-1.23727 1.04642,-2.4326 1.05182,-3.84575 -0.22131,-3.54194 -1.77267,-5.77613 -5.40918,-6.52543 -2.64401,-0.0941 -6.38144,0.38711 -4.12756,0.16196 -0.47675,2.79321 0.30356,5.32901 2.8092,7.0316 2.26114,1.13522 4.71213,2.15776 7.27649,2.15031 l -2.0021,1.4546 z"
- id="path5627"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -325.44177,-146.36408 c -2.15003,0.0939 -4.13397,-0.62274 -6.11548,-1.3684 -3.29807,-1.37358 -4.99286,-4.13578 -4.94919,-7.64209 0.95755,-3.73235 4.853,-3.46947 7.85364,-2.90418 2.71704,1.29254 4.14401,4.04373 4.86737,6.85059 0.21991,1.4565 -0.087,2.86377 -0.36978,4.27183 l -2.14511,0.99153 c 0.33014,-1.37126 0.69336,-2.74238 0.47812,-4.18885 -0.64691,-2.75957 -2.01975,-5.42569 -4.72476,-6.62326 -4.66456,-0.84861 -2.88779,-1.61513 -3.91913,0.58459 -0.006,3.4559 1.44803,6.0521 4.7799,7.40647 2.02115,0.76742 4.05799,1.39357 6.24656,1.16716 l -2.00214,1.45462 z"
- id="path5629"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -326.31271,-131.55795 c -2.32352,-0.68241 -4.16691,-2.64543 -6.05964,-4.1501 -2.43139,-1.63714 -2.76138,-3.88257 -2.18569,-6.57634 1.89752,-3.00371 4.72241,-3.75234 7.42456,-1.89988 2.54647,2.42659 3.83325,5.60318 3.80966,9.06459 -0.40155,2.61014 -1.88642,2.93683 -4.22074,4.22692 l 1.79825,-1.43953 c -1.38092,0.86333 -0.38397,0.76524 0.36469,-1.76613 0.12103,-3.40581 -1.07038,-6.57791 -3.67615,-8.88417 -3.45389,-2.08263 -2.77072,-1.14319 -3.41476,-0.27223 -0.66281,2.61378 -0.26804,4.69761 2.04953,6.32982 1.83038,1.53504 3.6781,3.61096 6.11239,3.91241 l -2.0021,1.45464 z"
- id="path5631"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -325.44177,-118.92922 c -2.29087,0.43588 -4.22995,-0.62734 -6.23395,-1.65117 -3.32593,-1.88845 -5.45053,-4.61769 -5.79343,-8.40089 1.30451,-3.87939 5.63459,-3.22186 8.63916,-2.15031 3.03772,1.52717 4.5788,4.57313 5.7435,7.61569 0.4303,1.85847 0.04,2.90249 -1.36599,4.01875 l -2.21442,0.85342 c 1.46652,-0.99012 1.93158,-1.91573 1.56576,-3.76859 -1.0816,-3.05772 -2.57589,-5.95961 -5.6116,-7.4542 -4.17068,-1.47998 -4.57077,-1.41296 -4.7052,-0.14 0.18515,3.72952 2.32996,6.39026 5.5968,8.21312 2.07538,1.04764 4.03772,1.93151 6.3815,1.40956 l -2.00213,1.45462 z"
- id="path5633"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -326.74817,-108.04237 c -2.13144,0.15303 -4.27087,0.31941 -6.39407,0.2098 -3.82999,-1.26017 -5.42429,-4.00184 -5.19831,-7.89866 1.33249,-4.16546 4.98145,-3.25258 7.97428,-1.97967 2.95068,1.11349 4.99301,2.89516 5.89328,5.86369 0.0993,0.61377 -0.14554,1.17962 -0.24985,1.76177 l -2.15774,1.0014 c 0.12545,-0.5533 0.40878,-1.08092 0.30978,-1.68055 -0.71039,-2.95908 -2.80536,-4.59745 -5.65121,-5.70536 -4.33872,-1.76047 -3.7941,-1.80373 -4.0784,-0.2683 -0.35318,3.74585 1.24752,6.56457 5.02035,7.59778 2.18399,0.0498 4.35968,-0.0968 6.534,-0.35652 l -2.00211,1.45462 z"
- id="path5635"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -326.74817,-96.284576 c -2.71124,0.169853 -4.98553,-0.9879 -7.12124,-2.554176 -2.07252,-1.920318 -3.86963,-4.198498 -1.49103,-6.429568 2.93972,-2.06337 5.72152,-1.97779 8.69564,-0.21969 2.38727,1.96882 2.60495,4.41428 2.91733,7.488065 -0.76162,1.811356 0.24137,0.245346 -2.08804,1.467316 l 0.64908,-0.198341 c -2.80356,1.751366 -1.08319,0.973732 -0.63809,-0.23275 -0.18216,-3.001945 -0.39682,-5.44549 -2.7645,-7.30234 -2.11069,-1.13769 -7.50339,-0.75476 -4.59784,-1.84964 -2.48712,1.99763 -0.79513,4.21247 1.19202,6.0998 2.19708,1.582011 2.98492,2.615619 5.74499,2.266536 l -0.49832,1.46479 z"
- id="path5637"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -303.2326,-165.08946 c -2.30146,0.11226 -4.28223,-0.70995 -6.25181,-1.81226 -3.04406,-1.97719 -4.4963,-4.69153 -3.97345,-8.29744 2.09388,-3.64215 5.90857,-3.83082 8.8476,-1.48716 2.35464,1.97892 2.7688,4.44047 2.24881,7.30601 l -2.08872,1.00667 c 0.59809,-2.77831 0.23795,-5.18797 -2.06408,-7.12158 -3.13266,-2.35903 -5.14948,-1.81157 -4.89862,-0.689 -0.64643,3.4922 0.86105,6.16724 3.82171,8.05261 2.0188,1.09177 4.02494,1.85179 6.36069,1.58753 l -2.00214,1.45462 z"
- id="path5639"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -301.96173,-151.88091 c -3.23566,2.11776 -5.93439,2.57847 -9.40015,0.96313 -2.64636,-2.64364 -3.19488,-6.32839 -2.10058,-9.7642 2.71914,-3.41378 7.09738,-2.59835 9.90011,0.16289 1.43351,2.62697 2.21817,5.50257 0.88993,8.21321 -0.18824,0.31133 -0.52207,0.47701 -0.78034,0.71297 l -2.19934,0.8714 c 0.26831,-0.21575 0.59158,-0.36846 0.80603,-0.64813 1.48297,-2.58741 0.84876,-5.41642 -0.6383,-7.98986 -2.7443,-2.57637 -6.24649,-3.0962 -5.89133,-2.26831 -1.2276,3.30445 -0.76947,6.9643 1.93423,9.47277 2.6713,1.22212 8.58417,-0.21122 5.19825,1.21917 l 2.28149,-0.94504 z"
- id="path5641"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -303.2326,-135.47723 c -1.28684,0.42855 -2.7211,0.35537 -4.07599,0.27037 -2.4505,-0.27416 -4.28617,-0.57978 -5.1485,-3.02603 -0.52008,-2.7146 -0.28348,-5.32157 0.77511,-7.84655 2.879,-3.16642 5.74414,-3.43191 8.4285,-0.36749 2.39609,2.99379 2.47653,5.68692 1.2977,9.12708 -0.19592,0.92506 -0.8251,1.39552 -1.41562,2.01683 l -2.16824,0.84112 c 0.63943,-0.56787 1.28857,-0.95807 1.5152,-1.8918 1.18484,-3.35232 1.27648,-6.0201 -1.15296,-8.94192 -2.73767,-3.0078 -4.48187,-2.25731 -4.39212,-1.72538 -1.13568,2.46076 -1.38211,5.0008 -0.84033,7.68483 0.99304,2.2508 2.55853,2.5699 4.99872,2.7788 1.41049,0.0428 2.82342,0.0331 4.18066,-0.37448 l -2.00213,1.45462 z"
- id="path5643"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -304.97449,-121.10659 c -1.52325,-0.0192 -3.03231,-0.023 -4.54574,-0.20264 -2.94635,-0.001 -4.40473,-1.59481 -5.08268,-4.28695 -0.19244,-3.85804 1.65015,-5.18389 4.86646,-6.92306 2.16379,-0.3102 3.8212,0.57054 5.1463,2.21045 1.27029,1.82603 1.85547,3.32303 1.6796,5.50922 -1.76807,2.74141 -5.38953,3.98389 -3.39798,2.85133 l -2.20572,0.85766 c 1.96768,-1.15382 2.12726,-0.72751 3.4906,-2.69846 0.21667,-2.12186 -0.35501,-3.56686 -1.58792,-5.35233 -1.38355,-1.46186 -2.84394,-2.39579 -4.97067,-1.92788 2.34094,-1.50733 -1.43253,1.2768 -0.98166,4.40106 0.65643,2.66646 2.07397,4.00664 4.94061,4.01445 1.54758,0.14187 3.0965,0.16223 4.65091,0.0925 l -2.00211,1.45462 z"
- id="path5645"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -305.40994,-107.17142 c -2.24579,-0.0107 -4.39759,-0.48523 -6.42899,-1.42073 -2.9667,-1.86281 -4.70816,-5.00529 -2.98465,-8.19439 3.30005,-2.38144 6.41297,-2.64199 9.45154,0.0978 2.73599,2.22536 3.42596,5.0672 3.57678,8.42175 -0.52801,2.25928 -1.98257,2.70301 -4.02957,3.86337 l 1.8183,-1.45744 c -3.14347,1.87217 -0.024,0.21607 0.15588,-1.36686 -0.0654,-3.29544 -0.73147,-6.1031 -3.41815,-8.28868 -2.20621,-1.8534 -7.25513,-2.65574 -5.43314,-2.17002 -1.90863,3.01438 -0.18588,6.1007 2.73832,7.85669 2.08001,0.9007 4.27716,1.36065 6.55578,1.20384 l -2.0021,1.45463 z"
- id="path5647"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -301.52676,-98.842331 c -1.64289,2.330704 -4.45768,2.673058 -7.09696,2.851814 -2.5932,0.347633 -4.36963,-0.46627 -5.91559,-2.488251 -1.49099,-3.709432 0.37909,-5.737442 3.58545,-7.514142 2.78293,-0.75397 4.52967,1.67271 6.34784,3.44244 1.09589,1.32596 1.06131,2.901353 1.01746,4.536378 l -2.09735,1.029808 c 0.11102,-1.575188 0.12143,-3.052616 -0.9177,-4.346666 -1.74295,-1.78142 -3.42608,-4.12053 -6.16092,-3.22586 2.52747,-1.56974 -1.47091,2.06296 0.24949,4.900407 1.58633,1.8965 3.1741,2.670746 5.73898,2.308281 4.47415,-0.350235 1.82294,0.29924 3.04428,-0.370705 l 2.20502,-1.123505 z"
- id="path5649"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -306.06479,-88.452766 c -2.37137,0.405823 -3.19968,0.945015 -5.61667,1.108086 -2.68084,-0.540382 -3.81705,-2.670792 -3.04699,-5.088615 2.43066,-2.102821 4.68991,-3.140137 7.72723,-2.508446 2.3981,0.903252 2.1691,3.006775 3.41476,5.102989 0.14905,0.246605 0.22541,0.52446 0.33814,0.786682 l -2.05032,1.141388 c -0.10685,-0.2537 -0.17756,-0.52256 -0.3205,-0.761055 -1.2048,-2.086319 -0.88791,-4.162575 -3.32352,-4.95356 -3.406,-0.570869 -3.988,0.219276 -3.61139,0.264175 -0.98907,2.238914 0.17585,4.249962 2.74436,4.655991 2.46832,-0.18232 3.32916,-0.730965 5.74701,-1.202255 l -2.00211,1.45462 z"
- id="path5651"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -254.02403,-137.6546 c 4.93484,-2.15924 -3.51839,2.15056 -6.14275,1.98608 -3.49197,-0.89235 -3.13916,-4.92892 -2.16516,-7.70854 2.40231,-4.44334 7.1938,-4.63672 9.3587,-0.25885 0.93178,1.92352 0.52019,3.55881 -0.28352,5.40981 l -2.02654,0.91066 c 0.84147,-1.7773 1.26793,-3.34432 0.38175,-5.23172 -1.96152,-3.8852 -4.91495,-4.33764 -5.40698,-1.76246 -0.98133,2.63504 -1.39273,6.53029 2.02282,7.29947 5.52735,0.18988 1.27237,0.0363 6.26378,-2.09907 l -2.0021,1.45462 z"
- id="path5653"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -237.9115,-143.75124 c -3.13071,0.21416 -4.554,-1.22763 -4.81894,-4.304 0.21242,-3.93683 6.09937,-8.44853 9.15807,-4.95807 1.27496,3.40052 -1.32782,6.20609 -3.6371,8.4489 l -2.03935,0.8365 c 2.27844,-2.11023 5.01613,-4.82678 3.73651,-8.13752 -3.25766,-2.65232 -4.27294,-1.14603 -5.2385,2.79698 0.26963,3.0831 1.78813,4.24789 4.84143,3.86259 l -2.00212,1.45462 z"
- id="path5655"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -254.89497,-126.33228 c -3.13329,0.0714 -5.78243,0.72538 -7.95522,-1.92212 -1.82278,-4.33618 3.3631,-8.66197 7.24674,-6.12251 2.0569,2.37606 1.24919,5.28838 0.63331,8.05152 l -2.07695,1.03155 c 0.68491,-2.69151 1.62506,-5.55478 -0.46728,-7.88038 -3.30011,-1.44057 -3.91593,-1.38926 -3.4108,3.7861 2.14264,2.54451 4.99173,1.82761 8.0323,1.60122 l -2.0021,1.45462 z"
- id="path5657"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -236.1696,-133.29987 c -2.06081,0.40278 -4.32861,0.38701 -6.44965,0.22509 -3.98565,-1.25196 -2.15142,-5.33884 0.3618,-7.24758 5.3918,-3.43671 7.34144,-0.52218 8.13115,4.74288 0.26526,1.7287 0.47322,1.55957 -0.52974,2.83468 l -2.15316,0.85379 c 1.1105,-1.16594 0.90846,-0.94859 0.72761,-2.66786 -0.21536,-1.72009 -0.31436,-4.34511 -1.80405,-5.53552 -0.93045,-0.74355 -4.37292,0.69279 -3.56834,-0.18541 0.34866,-0.38056 0.86133,-0.56893 1.29199,-0.85338 -2.47442,1.67915 -4.46963,5.57958 -0.57362,6.73422 2.18211,0.12085 4.43213,0.1505 6.56811,-0.35553 l -2.0021,1.45462 z"
- id="path5659"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -255.33044,-114.13901 c -2.59977,-0.15117 -5.22171,0.13988 -7.78467,-0.35665 -4.14746,-3.35969 0.0911,-6.8271 4.12927,-7.46687 4.09699,-0.30684 6.26788,1.5752 5.40721,5.71748 -0.78103,2.52207 -2.50841,3.21657 -4.75712,4.3274 l 1.93939,-1.4291 c -3.24973,2.11884 0.34497,-0.0425 0.79802,-1.93922 0.93974,-3.98797 -1.2881,-5.69929 -5.18541,-5.33439 -1.02271,0.20716 -3.78571,1.30539 -0.45813,4.8306 2.61844,0.35777 5.26591,0.29276 7.91354,0.19613 l -2.00211,1.45462 z"
- id="path5661"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -237.47603,-121.54207 c -3.27539,0.41739 -6.18026,0.35769 -7.76379,-2.87509 -0.68576,-4.24622 6.87938,-8.11687 9.62716,-4.86674 1.46714,4.99809 -2.41305,7.27457 -6.32817,8.90412 l 1.82756,-1.3471 c 1.87432,-1.3453 2.44721,-4.12627 2.5598,-6.43067 -3.15094,-2.35763 -5.64383,-1.31524 -5.73394,2.65776 1.5682,3.21423 4.63147,3.05995 7.8135,2.5031 l -2.00212,1.45462 z"
- id="path5663"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -253.62411,-103.97878 c -3.25791,2.1224 -6.78983,3.13632 -9.61277,0.1195 -0.72903,-4.11694 4.62903,-6.79949 8.14746,-6.37855 4.61347,2.99125 2.74128,6.36553 -1.27999,8.47466 l 1.82028,-1.4173 c -2.14753,1.39372 2.50882,-3.6896 -2.41712,-5.77811 -2.99279,-0.18015 -3.61602,-0.57454 -4.30768,3.95353 2.42728,2.5403 8.12853,0.64222 5.36831,1.97131 l 2.2815,-0.94504 z"
- id="path5665"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -235.73413,-110.21974 c -3.80809,0.16488 -5.71196,-0.1416 -7.13301,-3.9795 -1.04515,-4.71212 4.36275,-7.64048 8.02992,-4.86031 3.62541,5.41206 -0.40623,6.76315 -4.94582,8.76394 l 1.67823,-1.32542 c 1.83024,-0.8163 4.45398,-1.73095 1.34955,-6.2487 -3.41868,-2.50298 -4.05214,-2.23399 -4.22327,2.63887 1.41546,3.7329 3.5607,3.90362 7.24652,3.5565 l -2.00212,1.45462 z"
- id="path5667"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -251.92824,-95.663597 c -3.23572,1.766136 -6.52955,4.666954 -10.20793,3.278481 -4.11911,-4.146835 2.63049,-6.343437 6.05026,-7.659234 3.5329,-1.1276 4.71407,1.83748 2.54431,4.250604 -0.8069,0.459045 -1.61377,0.918083 -2.42065,1.377144 l 1.81264,-1.453857 c -0.51819,0.309944 -1.0364,0.619858 -1.55458,0.929787 2.26442,-2.146393 1.2294,-4.842758 -2.11414,-3.74939 -2.90836,1.118263 -5.15952,1.197601 -2.4057,5.069 2.65774,0.835617 7.71661,-1.415558 6.03084,-1.10437 l 2.26495,-0.938164 z"
- id="path5669"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -235.33939,-101.01364 c -3.03329,1.38671 -7.22239,2.266784 -8.05532,-1.84099 1.21148,-3.86882 7.70173,-8.35401 9.53567,-2.96305 0.48197,2.00986 -0.16321,3.22348 -1.35837,4.65601 l -2.18075,0.8776 c 1.28717,-1.32922 2.0184,-2.44062 1.55902,-4.45373 -1.47132,-4.22685 -4.44161,-2.75041 -5.57005,0.86266 0.75759,4.076273 5.2211,2.71044 8.00244,1.36005 l -1.93264,1.50145 z"
- id="path5671"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -254.89181,-83.577377 c -2.56418,0.608551 -5.12882,1.754044 -7.56022,0.63549 -1.76388,-4.873344 2.50296,-7.069839 6.30253,-8.45414 3.25668,0.445503 4.37657,2.808403 3.95967,5.824944 -0.066,0.316299 -0.28752,0.551407 -0.43126,0.827103 l -2.13704,0.93734 c 0.71382,-1.163406 -0.18889,0.440727 0.47458,-0.773476 0.52383,-2.912331 -0.55522,-5.102966 -3.71889,-5.475761 -0.41299,0.171097 -2.40137,0.928627 -2.48444,5.87709 2.39463,0.947861 5.10329,-0.155434 7.51537,-0.934654 l -1.9203,1.536064 z"
- id="path5675"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -235.73413,-92.365334 c -2.53725,0.957283 -5.28684,2.187607 -7.88673,1.178932 -0.49064,-3.933792 4.17417,-6.117462 7.1594,-7.553825 3.00016,-0.397964 3.49083,1.993134 3.37912,4.445595 l -2.08567,1.002281 c 0.23774,-2.371521 -0.25212,-4.507072 -3.12104,-4.047493 -1.64737,1.090561 -2.57971,3.0737 -3.3363,4.898743 2.69595,0.627739 5.283,-0.316666 7.89334,-1.378845 l -2.00212,1.454613 z"
- id="path5679"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -273.70111,-186.20085 c -0.14618,2.75388 -0.34232,4.06918 -2.39194,5.76121 -3.38312,2.131 -7.14544,3.80701 -10.9526,2.1484 -2.81791,-4.76999 3.54693,-7.29453 7.29888,-8.68172 2.94315,-1.09445 4.15397,-0.67883 6.05756,1.63309 l -1.96774,1.17715 c -1.82111,-2.22197 -3.05808,-2.45818 -5.8421,-1.44636 -1.61353,0.60208 -5.78104,1.78836 -3.60553,6.08332 2.61718,1.07154 8.38931,-0.12881 6.92688,-0.0937 2.02606,-1.56943 2.58996,-2.60138 2.56744,-5.28809 l 1.90915,-1.29329 z"
- id="path5681"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -265.78183,-182.50842 c -2.91748,-0.55348 -3.57092,-2.22368 -3.68936,-4.95108 1.23901,-4.42244 5.86972,-4.849 9.77701,-4.79055 2.75229,0.63297 2.82898,3.37046 2.73574,5.76915 -1.54184,3.06153 -5.28186,3.34716 -8.27539,3.99865 l 1.82199,-1.41646 c 4.49738,-0.82984 2.7826,0.50102 4.36664,-1.57447 0.14313,-2.22975 0.11249,-4.89225 -2.49228,-5.44364 -5.08749,-0.0897 -3.64019,-1.61992 -5.89737,2.43978 0.0818,2.64576 1.84122,4.65155 4.75751,4.86277 l -3.10449,1.10585 z"
- id="path5683"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -276.66867,-169.00871 c -1.99951,0.38206 -3.9165,1.16145 -5.95752,1.50169 -3.98404,-0.66202 -2.96564,-5.04851 -1.23053,-7.5567 3.03223,-2.68855 7.09494,-3.6742 9.99585,-0.72997 1.07315,1.98233 0.93133,3.7347 0.23614,5.74599 l -2.08224,0.94679 c 0.73679,-1.93081 0.97769,-3.61959 -0.10965,-5.54371 -2.44678,-2.30934 -6.52957,-1.80415 -6.00348,-1.2863 -1.69611,2.27594 -2.85547,6.61448 1.05695,7.03125 2.08761,-0.36937 4.02704,-1.20653 6.09658,-1.56366 l -2.0021,1.45462 z"
- id="path5687"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m -257.10843,-178.53406 c -2.1387,2.59874 -5.76114,3.86902 -9.09766,4.07666 -4.65506,-2.21457 1.5,-6.64007 3.75925,-7.94449 2.64841,-1.03756 5.01022,-1.68573 6.45246,1.0159 0.25653,1.6083 -0.48615,2.72069 -1.38593,3.95198 l -2.07452,0.87745 c 0.93606,-1.17763 1.64532,-2.16282 1.45688,-3.74601 -1.40464,-2.46153 -3.81116,-1.66105 -6.20938,-0.67808 2.84228,-1.69042 -4.55808,3.52665 -0.14313,5.17891 3.87146,-0.28936 4.93448,-1.17713 5.03702,-1.6088 l 2.20501,-1.12352 z"
- id="path5689"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- <g
- id="layer3"
- display="none"
- style="display:none">
- <g
- id="g15036"
- transform="matrix(0.748816,0,0,0.748816,-835.9539,-460.5673)">
- <path
- d="m 846,647.09448 c -2.51776,5.73474 0,12.21314 0,18 0,5 0,10 0,15 0,6.29041 3.3429,14.31421 1,19 -1.09168,2.18329 -10.72778,7.62915 -13.49725,9 -2.04126,1.01038 -10.13965,8.31848 -15.50275,11 -1.41278,0.70636 -2.66669,1.69373 -4,2.54059 -6.05273,3.84436 -14.12616,4.52252 -20,7.45941 -4.94397,2.47199 -6.03174,8 -12.52014,8 -6.61579,0 -12.38825,2.04584 -18.47986,-1 -9.1355,-4.56775 -20.54053,-8.24847 -30.51251,-9.40863 -11.10144,-1.29156 -18.94818,-1.6571 -28.48749,-8.08124 -6.11786,-4.11999 -14.22137,-5.25848 -21,-8.51013 -5.46429,-2.62115 -5.5694,-14.29181 -4,-19 2.95715,-8.87152 0.14386,-20.04815 -1,-29 -1.11902,-8.7572 -1.57599,-18.11249 -1.57599,-27 0,-11.00958 -10.37659,-6.29761 -13.42401,-12.45434 -0.68567,-1.38532 20.51355,-8.16724 23,-10.10157 4.96796,-3.86474 16.35651,-4.62231 22,-7.44409 4.82941,-2.41467 12.16187,-9 17.41638,-9 5.31287,0 4.88568,2.84894 0.58362,5 -5.06867,2.53436 7.85608,2.38135 9,2 3.93799,-1.31268 5.70782,-0.79327 4.55853,-6 -0.97693,-4.42602 3.69031,-2.62445 8.44147,-5 5.98151,-2.99072 14.4978,-1.37445 21,-3 7.20599,-1.80151 12.04755,-3 19.55096,-3 8.32966,0 13.65046,-3.59955 21.44904,-1 5.5307,1.84357 12.34412,8.95355 16.43164,13 4.39038,4.34619 7.40289,8.49506 11.56836,13 4.01123,4.33814 8.30249,7.81055 12,11.54565 3.3606,3.39472 8.86041,7.88459 12,9.45435 7.086,3.54303 -3.09723,9.09723 -5,11"
- id="path8053"
- inkscape:connector-curvature="0"
- style="fill:#fffae1;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 862,632.09448 c 4.72846,2.36426 -11.15094,9.71698 -12,10 -6.44568,2.14856 -11.96802,5.99823 -19,8 -7.96338,2.26691 -15.79938,7.26648 -24,10 -4.74646,1.58215 -10.38471,1 -15.41858,1 -2.03216,0 -8.78656,-9.20514 -10.58142,-11 -4.77063,-4.77063 -9.82159,-10.82159 -14,-15 -4.55646,-4.55646 -10.20209,-10.57019 -16,-14.47467 -3.67212,-2.47296 -6.95734,-5.99267 -3,-9.09137 3.8706,-3.03076 8.90887,-4.38836 13,-6.43396 8.08636,-4.04321 14.55151,-7.06561 22.50018,-11 5.20099,-2.57434 10.46424,-3.96441 14.49982,-8 4.52136,-4.52136 12.32935,-0.83532 16,1 4.82983,2.41492 6.19202,8.38397 8,12 1.42346,2.84693 6.13928,8.17353 8.47229,10.41364 3.71423,3.5664 6.38727,5.81585 11.11163,8.58636 3.93537,2.30774 9.54523,6.30933 12.41608,8.58625 3.8977,3.09137 7.58185,3.25006 5,8.41375"
- id="path8813"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18879);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 798,580.09448 c 2.78394,2.20801 -7.34869,8.67438 -10,10 -4.22449,2.11225 -8.88043,3.13245 -12.58106,6 -3.66662,2.84125 -13.91033,8.83045 -17.41894,10 -1,0.33331 -2,0.66669 -3,1 -4.20319,1.40106 -9.42548,2.06751 -13.42114,4.43396 -4.13379,2.4483 -7.6463,3.03046 -12.57886,3.03046 -5.6944,0 -11.46802,0.13507 -17,1.01013 -6.88965,1.08991 -13.58179,2.02124 -20.42371,3.03046 -5.58654,0.82409 -11.27765,1.61181 -16.57629,3.03045 -5.62555,1.50623 -9.81177,3.6604 -7,-2.02026 1.13684,-2.29675 7.08948,-2.84424 10,-4.5152 3.39191,-1.94732 8.81934,-3.53991 12.56616,-5 5.98737,-2.33325 14.08081,-2.64697 18.43384,-7 1.65656,-1.65655 13.39239,-5.16308 15.40625,-6 3.84247,-1.59686 6.16156,-2.09155 5.05078,2 -0.58111,2.14044 -6.55041,5 -3.03046,5 3.38935,0 8.14087,-0.52954 11.1117,-2 5.75183,-2.84698 3.68524,0.30274 2.46173,-4 -0.5448,-1.91601 -3.05988,-5 1.57886,-5 6.31024,0 14.9483,-1.26361 20.42114,-4 5.49371,-2.74688 12.77258,-2.18005 18.46973,-5 4.38745,-2.17169 10.22784,-3 15.53027,-3 4.66394,0 5.01007,4 0,4"
- id="path9582"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18881);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 779,740.09448 c -5.29309,-2.7251 -7,-12.2052 -7,-20.47973 0,-7.17341 0,-14.34687 0,-21.52027 0,-6.80749 1,-13.74585 1,-20 0,-6.6449 0.1864,-12.54278 1.40881,-18.5838 0.91974,-4.54529 2.69373,-8.4162 7.07105,-8.4162 1.89575,0 6.31097,8.80756 8.08124,10.43652 5.36871,4.94019 4.92712,4.06739 12.4389,1.56348 4.7102,-1.57007 9.72833,-4.59393 15,-4.59393 6.7785,0 10.88599,-3.1618 16.50275,-5.40607 4.91021,-1.96191 8.8844,-5 12.49725,-5 0.9389,0 -1,15.48676 -1,16.46698 0,6.323 0,12.73352 0,19.53302 0,7.43366 1.90344,10.86963 -2.40588,17 -3.90882,5.56061 -6.45911,6.96625 -11.59412,11 -5.82458,4.5755 -13.01746,7.87164 -19,12.57105 -6.42383,5.04602 -14.64459,7.07098 -21.42877,10.42895 -4.07483,2.01697 -7.2649,3.26349 -11.57123,5 z"
- id="path10350"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18883);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 772,642.09448 c 0.33331,1 0.66669,2 1,3 1.73468,5.20392 -1,12.94 -1,18.45679 0,9.0415 1,17.51691 1,26.54321 0,8.16852 -0.10175,16.33142 -3,23.45935 -2.03565,5.00641 0,15.04914 0,20.54065 0,8.46302 -11.92462,1.1507 -14,-3 -3.19775,-6.39557 -15.90515,-7.36615 -22,-8.44928 -8.02997,-1.427 -18.32983,-2.46808 -26,-5.05072 -6.47717,-2.18103 -13.72412,-4.14001 -19.45416,-7.5 -0.86328,-0.50622 -1.69721,-1.06091 -2.54584,-1.59143 -5.55389,-3.47168 -6,-7.04669 -6,-13.40857 0,-5.66357 -1.17596,-11.43628 -2,-17 -0.95612,-6.45569 -0.56586,-12.18927 -0.56586,-18.5838 0,-7.15308 -0.43414,-14.39307 -0.43414,-21.4162 0,-10.06964 0.93121,-11.83069 10.53571,-15 6.31988,-2.08545 14.06244,-3.43872 20.46429,-4.49499 6.67456,-1.10126 13.90863,0.62457 20.4469,-1.01014 5.26788,-1.31713 13.69299,-1.42492 17.5531,0.50513 4.59363,2.29682 7.57556,7.28778 13,10 3.76874,1.8844 7.05072,8.05066 10,11 1,1 2,2 3,3 z"
- id="path11118"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18885);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 710,638.09448 c 1.77734,4.51972 1,10.08661 1,15 0,6.67578 -7.36859,6.4162 -12,6.4162 -4.59961,0 -9.60278,-2.73059 -11.46429,-6.4162 -2.33332,-4.61963 0.34875,-12.17261 4.04058,-14 4.18146,-2.0697 7.17621,-2 12.42371,-2 3.23798,0 6,1.7804 6,4"
- id="path11892"
- inkscape:connector-curvature="0"
- style="fill:#afe0f0;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 754,642.1488 c 2.0575,5.94446 1.15759,13.26624 1.15759,19.72846 0,8.78015 -8.53003,8.43872 -13.89135,8.43872 -5.32459,0 -11.1164,-3.59131 -13.2713,-8.43872 -2.70105,-6.07593 0.40374,-16.00983 4.67749,-18.41321 4.84051,-2.72217 8.30725,-2.63049 14.38189,-2.63049 3.74829,0 6.94568,2.34161 6.94568,5.26092"
- id="path12668"
- inkscape:connector-curvature="0"
- style="fill:#afe0f0;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 704.60889,638.89978 c 3.04516,1.49817 0.53772,15.61786 -5.15625,11.41577 -4.49421,-3.31659 -6.80384,1.87915 -10.69422,-3.80524 -6.22058,-9.08911 5.81751,-11.41583 8.97541,-11.41583 3.6109,0 9.7044,4.33228 7.73449,10.1474"
- id="path13430"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18887);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 746.71875,646.51031 c 1.52801,0 1.45294,10.14734 -4.29688,10.14734 -5.41412,0 -9.05023,4.50116 -14.60955,1.26844 -0.72077,-0.41907 -1.43952,-0.84558 -2.15924,-1.26844 -3.83399,-2.25225 -5.19117,-13.95263 0,-13.95263 6.43829,0 12.7116,0 19.34692,0 5.28589,0 1.31195,11.71606 0,12.68426"
- id="path13432"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18889);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 702,714.56403 c 0,5.04931 -10.84161,0.55566 -13,-1.46955 -4.35315,-4.08453 -4,-8.44269 -4,-14 0,-5.53875 1.79767,-10.82092 3,-16 1.11749,-4.81347 1.64038,-6.91314 7,-6 0.68603,0.11688 1.33331,0.39929 2,0.59894 3.28589,0.98407 4,10.22998 4,13.40106 0,5.00653 -1.11395,11.77204 1,16 1.81995,3.63996 0,8.52979 0,10.5"
- id="path14230"
- inkscape:connector-curvature="0"
- style="fill:#a99669;fill-opacity:0.49773799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 763,725.09448 c 1.1875,2.37494 -2.18951,8.09479 -6,10 -7.49805,3.74902 -13.49805,-1.85809 -19,-4 -3.26941,-1.27276 -9.13672,-4.13629 -10,-8 -0.98676,-4.4162 0,-10.46655 0,-15 0,-6.53064 0.62952,-12.64721 -0.56329,-19 -0.97004,-5.16638 0.82788,-9.13226 4.56329,-11 1.62067,-0.81036 9.81531,4.40766 11,5 4.41809,2.20905 12.1709,1.08545 16,3 3.51202,1.75598 2,11.57044 2,15 0,5.40027 -1,11.34534 -1,17 0,2.86292 0.63977,5.3017 3,7 z"
- id="path14232"
- inkscape:connector-curvature="0"
- style="fill:#a99669;fill-opacity:0.49773799;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 723.86603,598.40173 c -3.40198,0.98639 -6.71527,2.98828 -9.92407,4.64514 -5.37867,2.53602 -10.92591,4.68268 -16.71662,6.0459 -6.94,1.72876 -13.7381,4.10895 -20.31604,6.91498 3.22693,-2.62237 -14.65106,7.86694 -11.05432,5.7804 10.94952,-6.35199 5.52509,-3.14081 3.04181,-1.38349 -3.77124,3.86744 0.21527,3.88715 3.77081,4.23243 5.64954,0.91229 6.09186,5.9179 6.62628,10.87793 0.28315,4.7586 0.2425,9.52972 0.25086,14.29492 -0.006,5.03369 0.0759,10.07318 -0.22363,15.09228 -0.55823,6.41669 -1.27723,12.81958 -1.66303,19.25043 -0.47369,6.02325 -0.17133,11.88739 1.67157,17.60339 2.32562,3.68762 5.93774,5.75061 9.86481,7.33655 4.52826,1.98297 9.01544,3.97864 13.77459,5.33008 5.15998,0.87445 10.19776,2.22644 15.22907,3.64075 4.67859,1.43127 9.29919,3.02471 13.78296,4.97265 4.0224,2.55384 8.47534,4.20026 12.96539,5.70819 4.62616,1.05066 9.01184,2.79438 13.47492,4.34809 5.1391,1.14929 9.93219,3.28033 14.70276,5.45166 1.10199,0.5064 2.32354,0.59277 3.48053,0.88787 l -6.78894,4.7074 c -1.16254,-0.35559 -2.3808,-0.54004 -3.48884,-1.0672 -4.72674,-2.20044 -9.47174,-4.31091 -14.59234,-5.44409 -4.45801,-1.55164 -8.90326,-3.10614 -13.47302,-4.31976 -4.5011,-1.61011 -9.05622,-3.15833 -13.10407,-5.76636 -4.44232,-1.87878 -8.99774,-3.52087 -13.62268,-4.91266 -5.04822,-1.33795 -10.06714,-2.74567 -15.21808,-3.638 -4.80493,-1.3468 -9.28198,-3.48206 -13.91931,-5.31403 -4.07208,-1.76025 -7.76923,-4.03253 -10.20947,-7.88555 -1.6933,-5.88764 -2.10645,-11.92951 -1.59424,-18.08063 0.36548,-6.4198 1.04358,-12.80994 1.50055,-19.22278 0.33209,-4.9848 0.18548,-9.98047 0.19323,-14.97955 -0.006,-4.74982 0.0315,-9.50403 -0.20715,-14.24909 -0.44006,-4.57776 -0.66925,-9.32928 -6.06872,-9.84845 -4.31342,-0.47625 -8.4538,-1.26654 -4.64911,-5.98278 20.05066,-14.97553 7.1311,-2.35144 21.84936,-12.41388 6.58283,-2.6803 13.35785,-4.94739 20.25385,-6.66828 5.82977,-1.44739 11.41198,-3.79864 16.81726,-6.40661 3.38379,-1.74695 6.81134,-3.91993 10.54937,-4.59919 l -6.9663,5.06134 z"
- id="path8021"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 775.4541,738.80237 c 3.62226,1.03564 6.82727,-0.17963 10.29389,-1.25922 5.41504,-1.38086 10.255,-4.19153 15.28405,-6.52911 11.2782,-6.01819 -4.19415,4.0614 8.4267,-5.56659 5.23981,-4.14124 28.8125,-16.43549 17.51367,-10.13294 3.94043,-2.56994 7.55298,-5.6311 10.92316,-8.85949 3.21979,-4.38196 3.72552,-9.43616 3.99262,-14.68262 0.3457,-5.13556 0.92804,-10.23102 1.09356,-15.38599 0.0695,-6.12341 0.10712,-12.24731 0.12012,-18.37115 -0.022,-3.90277 -0.0683,-7.80664 -0.12457,-11.70795 l 7.31481,-3.72711 c -0.0678,3.94403 -0.0937,7.89062 -0.12634,11.83606 0.009,6.11182 0.0765,12.22406 -0.0173,18.33551 -0.1004,5.1557 -0.55615,10.25049 -1.03327,15.37317 -0.27642,5.32117 -0.65557,10.48456 -3.70752,15.06708 -3.24292,3.47284 -6.96417,6.53912 -10.94989,9.22717 -16.6026,9.76056 -14.6643,7.57428 -17.48095,10.0423 -6.9563,5.16943 -13.9867,10.31482 -22.14411,13.49884 -5.08886,2.40985 -10.03576,5.17559 -15.5321,6.58435 -3.62158,1.09271 -7.02832,1.87036 -10.81287,1.31903 l 6.96631,-5.06134 z"
- id="path8023"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 851.21558,643.84802 c 3.7312,-1.69269 7.70953,-3.2345 11.52826,-4.88525 -0.64325,0.55334 2.73016,-1.49829 2.04956,-1.12024 -9.08448,5.04559 -2.96772,2.48254 -6.45636,-4.21289 -4.53424,-3.6344 -8.92603,-6.98145 -12.71192,-11.43537 -4.30353,-5.00872 -9.11181,-9.55719 -13.51404,-14.46795 -3.59954,-4.42975 -7.65283,-8.43891 -11.83685,-12.30439 -3.64245,-3.52221 -7.56714,-6.69018 -11.92621,-9.26617 -4.51782,-2.76868 -9.48938,-2.88232 -14.62445,-2.87006 -7.55511,1.66108 -14.74481,4.66071 -22.29681,6.56263 -6.47077,1.05475 -12.98633,1.86694 -19.54132,2.19781 -4.80591,0.20789 -9.45508,1.12952 -14.05658,2.4831 l 6.28637,-4.84119 c 4.66779,-1.35889 9.3833,-2.29279 14.26685,-2.40076 6.46857,-0.31586 12.92285,-1.07208 19.29657,-2.20642 7.63446,-1.88751 14.85907,-5.13007 22.54333,-6.56219 5.2793,0.1477 10.36249,0.18981 14.93262,3.20288 4.37854,2.64843 8.31232,5.91949 12.0224,9.44494 4.14539,3.94177 8.23639,7.95258 11.74713,12.4859 4.4029,4.90595 9.22638,9.42231 13.51868,14.43177 3.85168,4.37329 8.33142,7.71576 12.85687,11.34448 5.20557,8.1004 -0.44592,10.57123 -8.88043,14.53168 -4.04089,1.66168 -8.05115,3.68353 -12.17004,4.94903 l 6.96637,-5.06134 z"
- id="path8027"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 773.5144,743.02014 c -3.21447,-1.79443 -2.72924,-5.84363 -2.90661,-9.41485 -0.14386,-5.57807 -0.097,-11.15796 -0.0758,-16.73707 0.35962,-5.7995 0.80835,-11.5874 0.93799,-17.40533 -0.31976,-5.52032 -0.80072,-11.02936 -0.93555,-16.56738 -0.19415,-4.80432 -0.069,-9.55957 0.50306,-14.32752 0.48974,-5.22149 0.45288,-10.47461 0.45648,-15.71399 0.0472,-2.78241 -0.0332,-5.56793 -0.0963,-8.34393 l 4.83569,-2.52069 c 0.0761,2.80225 0.11468,5.61133 0.0635,8.41901 -0.0343,5.24926 0.0449,10.51245 -0.39667,15.74713 -0.56494,4.75201 -0.74597,9.48419 -0.54681,14.27484 0.0981,5.55462 0.50964,11.07892 0.88208,16.61249 -0.0892,5.82568 -0.53144,11.6159 -0.97748,17.41711 0.0278,5.55817 0.0262,11.11768 0.15564,16.67438 0.16461,3.32495 -0.36328,7.22247 2.74493,8.51159 l -4.64423,3.37421 z"
- id="path8029"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 856.34692,642.00488 c -7.42901,2.1399 -14.25891,5.96314 -21.30279,9.10852 -6.67206,3.06293 -13.64374,5.3003 -20.5517,7.72925 -4.75171,1.84534 -9.37799,3.99518 -14.0047,6.10437 -4.85949,2.28711 -7.77826,1.58234 -11.88934,-1.44446 -3.36041,-3.22216 -6.16937,-6.93652 -9.23956,-10.42199 -3.01215,-3.16205 -5.75428,-6.58228 -8.53962,-9.94788 -2.46606,-3.15894 -5.37487,-5.8493 -8.46142,-8.37268 -2.88159,-2.55347 -5.84711,-5.00592 -8.76007,-7.52625 -2.96955,-3.72143 -6.57294,-6.79504 -9.97162,-10.10357 -0.2992,-0.2829 -0.59833,-0.56586 -0.89746,-0.84876 l 4.55639,-2.80743 c 0.30152,0.28931 0.60303,0.57861 0.90448,0.86792 3.4621,3.24298 7.06598,6.30475 10.05554,10.00476 2.91199,2.51392 5.90046,4.93189 8.78296,7.47797 3.08936,2.56006 5.9925,5.30682 8.43781,8.51062 2.80041,3.34997 5.57574,6.73358 8.56457,9.91479 3.05255,3.4754 5.78369,7.23816 9.19074,10.38446 4.06354,2.86066 6.87793,3.12671 11.5509,0.96027 4.57868,-2.15491 9.20075,-4.24329 13.935,-6.05115 6.85144,-2.31769 13.70825,-4.60156 20.31329,-7.58429 7.25031,-3.25299 14.28217,-7.21063 21.97083,-9.32867 l -4.64423,3.3742 z"
- id="path8033"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 746.00879,612.3667 c 1.50897,-0.52124 -5.46747,3.29474 -4.1413,2.40582 11.87702,-7.96124 0.0208,0.017 14.1435,-7.79736 5.92602,-3.02972 11.86261,-6.03876 17.77856,-9.08801 8.73676,-5.35163 17.35394,-10.85914 26.25971,-15.92932 10.85528,-5.86768 -3.9917,2.62152 6.97155,-3.61359 l -4.36425,3.49261 c 11.00604,-6.42175 -3.83252,2.14991 -6.74933,3.46253 -5.82416,3.53644 -11.5614,7.2146 -17.37751,10.76434 -8.71813,5.04437 -17.63372,9.69483 -26.62817,14.22412 -3.86987,2.16291 -8.755,5.14673 -10.53699,5.45307 l 4.64423,-3.37421 z"
- id="path8035"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 741.18951,617.76123 c -3.68096,-2.08527 -7.74273,-1.60675 -11.82788,-1.31635 -5.28088,0.87079 -10.48779,2.06037 -15.78564,2.87287 -5.77344,1.06152 -11.54254,1.85455 -17.39258,2.32465 -5.67773,0.268 -11.16126,1.60522 -16.72003,2.60089 -4.44629,0.22479 -8.82245,0.91198 -13.25104,1.31372 -0.32056,0.0154 -0.64117,0.0309 -0.96173,0.0463 l 4.38946,-3.22467 c 0.32007,-0.0184 0.64008,-0.0369 0.96015,-0.0553 4.40692,-0.45801 8.78583,-1.04615 13.20953,-1.33948 5.52002,-1.11859 11.03522,-2.11701 16.66663,-2.53363 5.83838,-0.44782 11.59503,-1.31677 17.36072,-2.34137 5.29229,-0.82807 10.48455,-2.12787 15.79248,-2.83838 4.17956,-0.24127 8.30102,-0.50495 12.20416,1.11651 l -4.64423,3.37421 z"
- id="path8039"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 690.99176,633.47858 c -1.28192,1.54498 -1.22919,3.46368 -1.20508,5.39318 0.0358,2.75843 0.002,5.5185 0.14026,8.27125 0.0841,2.39935 0.46192,4.53173 1.6886,6.45556 2.13336,1.13526 4.46631,1.7533 6.73462,2.54578 2.91791,1 5.78125,2.11645 8.7348,3.00488 6.60046,-0.12366 -0.52472,3.15656 1.85437,0.56891 0.6048,-2.09326 0.49176,-4.31745 0.51282,-6.4798 0.009,-2.35382 0.007,-4.7077 0.008,-7.06152 0.24041,-2.70544 -0.21015,-5.15729 -1.46485,-7.51202 -1.78552,-1.95002 -4.12598,-2.51716 -6.64154,-2.93634 -3.62829,-0.16132 -7.2879,0.2807 -10.88367,0.75164 -1.18823,0.12549 -2.37841,0.24952 -3.56604,0.36579 l 3.18256,-2.37476 c 1.20221,-0.11468 2.40875,-0.2005 3.60046,-0.40466 3.62,-0.33533 7.27344,-0.86627 10.90796,-0.62628 2.64179,0.46081 5.0929,1.02551 6.92603,3.13055 1.2113,2.44849 1.75757,4.97974 1.56811,7.75702 -1.8e-4,2.35419 10e-4,4.70831 -0.005,7.0625 -0.015,2.19916 0.0748,4.4361 -0.3899,6.5957 -1.95984,3.1864 -5.35241,4.73114 -8.87512,3.40686 -2.96881,-0.90948 -5.86597,-2.00995 -8.81775,-2.97333 -2.31396,-0.81195 -4.71106,-1.44244 -6.88177,-2.60833 -1.41291,-1.99579 -1.60053,-4.32495 -1.75611,-6.80579 -0.18799,-2.75616 -0.0986,-5.52148 -0.18725,-8.28479 -0.0313,-1.9364 -0.11548,-3.85925 0.84686,-5.5979 l 3.96924,-1.6441 z"
- id="path8041"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 726.6615,642.0882 c 5.32782,-3.91571 3.12811,-0.3415 3.09155,3.68469 -0.0491,2.87964 -0.0684,5.75134 0.19202,8.62024 0.25275,2.55261 0.23993,5.13061 0.44733,7.67401 1.2655,2.45868 3.79803,3.5672 6.21942,4.64636 3.11413,1.28815 6.38647,2.01178 9.68237,2.64044 2.80011,0.52741 5.64038,0.77741 8.45264,1.21472 5.14978,-0.8894 -0.26703,3.89216 1.40289,-1.294 0.44092,-2.40247 0.0558,-4.81268 -0.10608,-7.2207 -0.21289,-3.30256 0.10834,-6.58686 0.30237,-9.87946 0.4082,-3.33453 -0.38507,-6.02045 -2.21893,-8.75153 -1.99598,-2.2752 -4.73102,-2.60638 -7.58484,-2.83184 -3.36853,-0.20099 -6.74317,0.0402 -10.0943,0.38622 -4.45984,0.0725 -11.15753,3.17005 -6.30054,0.77582 -0.25586,0.2107 -0.427,0.49445 -0.6405,0.7417 l -3.74353,1.61615 c 0.20276,-0.2655 0.35602,-0.56891 0.60834,-0.79657 4.32116,-2.7323 8.0025,-4.46051 13.2008,-4.65857 3.39893,-0.3277 6.80914,-0.65594 10.2254,-0.39978 2.96436,0.26294 5.77405,0.67786 7.74915,3.1399 1.90045,2.82055 2.79108,5.544 2.33533,8.99926 -0.20844,3.26642 -0.51813,6.5282 -0.2439,9.80335 0.14917,2.43176 0.59546,4.85333 0.18628,7.28607 -0.97223,3.70398 -4.46448,6.19177 -8.3819,5.40625 -2.80859,-0.42047 -5.63964,-0.69989 -8.42785,-1.25494 -3.31165,-0.63648 -6.5716,-1.46387 -9.71143,-2.71497 -2.54565,-1.16742 -5.19421,-2.29767 -6.51477,-4.90973 -0.21814,-2.57226 -0.18585,-5.18304 -0.38422,-7.77258 -0.29937,-2.87506 -0.28784,-5.7475 -0.24585,-8.63819 0.0588,-4.44989 -0.93603,-6.05169 3.98597,-8.04296 l -3.48322,2.53064 z"
- id="path8043"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 684.10236,717.51782 c 0.36175,-5.40777 0.6184,-10.83154 0.92419,-16.2467 0.32819,-6.47772 0.39905,-12.96967 0.31781,-19.45484 -0.60937,-3.09564 0.25452,-11.01934 5.36566,-7.5523 2.0058,1.08435 4.34522,1.60394 6.59528,1.99359 3.26501,-0.0712 4.67883,1.75396 5.67749,4.60937 0.59827,3.61072 0.17828,7.29615 -0.0642,10.92566 -0.97412,7.08765 -0.29669,14.19208 0.47186,21.25568 0.17231,2.28613 0.35352,4.56848 0.59528,6.84619 l -3.51801,1.84491 c -0.17529,-2.33093 -0.1477,-4.67035 -0.32305,-7.00281 -0.48249,-7.07208 -1.24775,-14.1239 -0.66138,-21.2229 0.12543,-3.57098 0.61401,-7.18548 -0.0489,-10.72534 -0.95618,-2.81171 -2.3183,-4.24115 -5.44696,-4.19495 -2.30579,-0.45807 -4.74298,-0.92614 -6.76764,-2.13433 2.23004,-2.49842 1.711,-2.52643 1.47839,3.64965 -0.1239,6.53095 -0.10156,13.06751 -0.53778,19.58887 -0.3255,5.271 -0.67382,10.59699 -0.22192,15.86566 l -3.83618,1.95459 z"
- id="path8047"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 725.51862,729.13458 c 0.48004,-4.27093 0.66552,-8.56042 0.77282,-12.85614 0.20709,-6.76031 0.12744,-13.51757 0.0557,-20.27929 0.32611,-5.44733 -0.88733,-10.85309 -0.77228,-16.26776 4.63403,-3.98536 5.40423,-1.87238 9.79266,0.19384 3.55915,1.63343 7.40534,1.94269 11.26105,2.08319 3.30615,0.46998 6.6524,0.36658 9.96216,0.74341 2.93658,0.2345 5.74804,0.78485 7.7359,3.02002 1.27881,4.35047 0.90759,9.12195 0.48511,13.60944 -0.53669,4.73413 -0.97181,9.47735 -1.4148,14.22107 -0.21411,4.65826 -0.62982,9.3045 -0.8092,13.96441 -0.22126,4.10236 0.14911,8.18122 1.02234,12.18543 l -3.54535,1.89032 c -0.71857,-4.07568 -1.10224,-8.21246 -0.88428,-12.354 0.17218,-4.67414 0.53479,-9.34095 0.84686,-14.00629 0.4046,-4.74835 0.7558,-9.49744 1.39893,-14.22266 0.37781,-4.35425 0.88244,-9.06433 -0.59656,-13.22735 -2.07129,-1.96503 -4.59637,-2.62458 -7.48675,-2.7732 -3.29975,-0.29132 -6.61719,-0.36316 -9.91108,-0.74707 -3.8905,-0.20996 -7.76715,-0.57019 -11.36889,-2.18597 -4.16675,-1.95508 -6.67865,-2.84528 -2.88361,-4.138 -0.67499,5.41455 0.80493,10.87982 0.42273,16.34985 0.0315,6.77851 0.0451,13.54932 -0.12994,20.32502 -0.0168,4.17138 -0.22168,8.35754 -0.11731,12.51715 l -3.83619,1.95459 z"
- id="path8051"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <g
- id="g8002"
- transform="matrix(0.92203,0,0,1.099622,137.1455,21.88672)">
- <path
- d="m 650.20831,521.21082 c 1.00379,0.67834 1.79193,2.76013 2.47016,3.90747 0.6997,1.18371 3.80566,1.80633 0,2.5401 -1.58228,0.30505 -0.9513,2.00909 -3.7052,2.5401 -2.73597,0.52752 -5.23944,1.07068 -7.98706,1.07068 -3.82148,0 -1.42963,-2.83674 -0.4159,-3.61078 1.56531,-1.19513 2.146,-2.58197 2.63934,-4.24732 0.31025,-1.04724 -5.53509,-0.94299 -6.38214,-0.94299 -2.70367,0 -5.37915,-1.97205 -5.96844,-3.77185 -0.61548,-1.87964 1.54413,-3.64227 2.64154,-5.02918 1.64374,-2.07751 6.89197,-3.91839 9.70904,-4.71487 2.78449,-0.78726 5.13514,-1.96033 7.41034,-2.82889 2.78321,-1.0625 5.48841,-1.36194 8.23377,-1.88595 3.04156,-0.58057 2.6864,0.97491 3.29346,2.82891 0.50183,1.5325 0.41174,3.08686 0.41174,4.71485 0,1.47513 -3.45453,3.66238 -4.52857,5.02914 -1.46643,1.86609 -5.53863,2.65717 -7.82208,4.40058 z"
- id="path7986"
- inkscape:connector-curvature="0"
- style="fill:#cedddf;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 660.08881,504.55167 c -0.62421,0.74472 -2.51496,3.84033 -3.7052,5.65783 -0.89191,1.36191 -3.46253,2.32932 -4.52857,3.14322 -1.52399,1.16358 -2.6286,2.10364 -4.5285,2.82892 -2.49298,0.95166 -3.67731,1.57159 -6.75623,1.57159 -2.38672,0 -4.91259,0.62867 -7.48559,0.62867 -2.75324,0 -3.96918,1.68255 -0.57892,2.20025 2.37884,0.36328 4.45856,0.57593 6.40106,0.94299 2.41162,0.45569 5.64246,0.29834 7.59625,0 3.2359,-0.49414 4.57312,-1.36138 6.58704,-2.51458 2.81018,-1.60919 5.33398,-2.17292 6.99866,-4.71485 1.27942,-1.95367 1.23504,-3.37423 1.23504,-5.65783 0,-1.39221 -2.31408,-7.38156 -1.23504,-4.08621 z"
- id="path7988"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient18891);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 662.76746,503.00867 c -0.14667,1.69918 -0.11756,3.40973 -0.10199,5.11313 0.0968,1.81704 0.0158,3.5914 -0.49079,5.36691 -0.77099,1.76068 -2.58215,2.927 -4.35522,4.11004 -2.61072,1.87946 -5.42951,3.3371 -8.53778,4.69855 -2.64624,0.76947 -5.4859,1.11249 -8.28168,1.41815 -2.75592,0.0864 -5.41986,-0.48333 -7.948,-1.27508 -2.64685,-0.86316 -4.24231,-1.60584 -3.4118,-3.92627 1.1026,-2.07611 3.19098,-3.69061 4.92358,-5.46265 1.16937,-1.35739 2.73389,-2.36175 4.4599,-3.27597 3.20349,-1.62463 6.74555,-2.7279 10.25104,-3.89276 2.59784,-0.83218 5.18317,-1.61844 7.89917,-2.19797 2.00079,-0.34466 4.03375,-0.57211 6.06457,-0.77615 l -2.37152,1.36105 c -2.00842,0.17227 -4.00909,0.40125 -5.97821,0.75409 -2.70086,0.58646 -5.25818,1.41712 -7.86108,2.2128 -1.57532,0.52597 -9.0437,3.39789 -4.375,1.32639 0.29602,-0.13135 -1.16473,0.52652 -0.88757,0.39471 -1.72614,0.89169 -3.30902,1.86996 -4.4997,3.19897 -1.72656,1.7688 -3.87298,3.3498 -4.98071,5.41926 -0.85468,2.2044 0.68762,2.79834 3.20459,3.62371 2.49536,0.77948 5.1289,1.35273 7.85119,1.18543 2.79804,-0.2909 5.6438,-0.63452 8.24494,-1.50482 -4.76227,2.12603 2.44409,-1.36066 3.59125,-2.4621 1.72192,-1.15106 3.45233,-2.28857 4.30896,-3.95496 0.56488,-1.75702 0.69049,-3.51696 0.57623,-5.32843 0.0152,-1.67678 0.0474,-3.36178 -0.10199,-5.03378 l 2.80762,-1.09225 z"
- id="path7990"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 661.104,503.3262 c -0.40063,1.25669 -0.9646,2.47855 -1.65191,3.66431 -1.05176,1.61001 -2.46094,3.06805 -3.81208,4.53729 -1.12622,1.25907 -2.53613,2.2797 -4.10754,3.19821 -2.53534,1.4021 -5.39185,2.14429 -8.3169,2.87525 -2.07281,0.60431 -4.19384,1.07263 -6.3283,1.52221 -2.00989,0.28675 -4.01618,0.51654 -6.06049,0.58466 -0.31048,0.009 -0.62115,0.0126 -0.93176,0.0189 l 2.39099,-1.33301 c 0.30847,-0.007 0.61712,-0.009 0.92541,-0.0198 2.03223,-0.0964 4.04047,-0.32312 6.03681,-0.6283 2.13495,-0.43988 4.23883,-0.93951 6.30896,-1.53857 3.23047,-0.79578 5.93097,-1.76575 3.17694,-0.63703 1.60162,-0.90759 3.06604,-1.89367 4.21564,-3.16089 1.34845,-1.45898 2.74127,-2.91372 3.83355,-4.49646 0.65638,-1.12164 1.22425,-2.3117 1.51306,-3.49453 l 2.80762,-1.09223 z"
- id="path7992"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 645.76593,522.09448 c -0.0325,0.9881 -0.0805,1.96759 -0.21838,2.94721 -0.42786,1.52002 -1.55671,2.65466 -3.01569,3.71063 -1.57446,1.00415 -2.16186,0.87592 0.16852,0.73389 2.73138,-0.14338 5.46502,-0.0485 8.18658,-0.2873 3.9408,-1.34393 0.89838,1.1977 2.31726,-1.57043 -0.41266,-0.96375 -1.46948,-1.70984 -2.27588,-2.51709 -0.62811,-1.0459 -1.58349,-1.74915 -2.77844,-2.42761 l 2.31677,-1.08911 c 1.20679,0.69409 2.15577,1.45672 2.83222,2.49279 0.85693,0.82593 1.96625,1.57166 2.37585,2.58881 -0.53723,2.23157 -4.10412,3.22577 -6.89813,3.6955 -2.73993,0.19482 -5.48926,0.13641 -8.23248,0.30163 -2.44416,0.21198 -2.48328,-0.1618 -0.54303,-1.15301 1.47046,-1.00995 2.66186,-2.09345 3.08191,-3.59473 0.15094,-0.94665 0.24432,-1.89624 0.10644,-2.8548 l 2.57648,-0.97638 z"
- id="path7994"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 635.95013,518.12927 c 1.42035,-1.79339 1.53711,-3.94641 1.35645,-6.00006 -0.0948,-1.95001 -0.84131,-3.70563 -2.01056,-5.39187 2.01251,-1.70777 3.79572,-1.20472 6.47345,-0.92011 2.34741,0.21588 4.6676,0.14954 6.99023,-0.13724 0.87287,-0.1261 1.73035,-0.36093 2.57026,-0.55368 l -1.54212,0.93424 c -0.82422,0.15796 -1.66571,0.33618 -2.50421,0.47165 -2.33215,0.26739 -4.66138,0.30969 -7.01251,0.0965 -2.034,-0.21905 -5.49762,-0.53952 -3.28772,-0.57834 1.07,1.71378 1.80609,3.50772 1.88781,5.45813 0.15235,1.99643 0.12018,4.21018 -1.16632,5.93814 l -1.75476,0.68268 z"
- id="path7996"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 637.24432,505.30795 c 1.45752,0.71906 2.96698,1.40903 4.4361,2.12915 2.83026,1.32205 5.92047,2.26206 8.94696,3.28269 1.5614,0.62137 3.28717,0.90625 4.97522,1.21161 l -1.55933,0.79864 c -1.66491,-0.32434 -3.34985,-0.64239 -4.88507,-1.26413 -3.02929,-1.04376 -6.164,-1.94165 -9.00311,-3.27585 -1.45923,-0.70605 -2.96399,-1.44037 -4.50403,-1.99826 l 1.59326,-0.88385 z"
- id="path7998"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- </g>
- <g
- id="layer16"
- display="none"
- style="display:none">
- <g
- id="g35215"
- transform="matrix(0.714055,0,0,0.714055,-893.0793,-488.597)">
- <path
- d="m 551,624.60712 c -5.7038,-0.39466 -19.39819,-3.57068 -28,-7.07111 -4.06238,-1.65314 -6.62293,-4.98248 -11,-6.44153 -6.14603,-2.0487 -10.15774,-2.31549 -13,-8 -2.01254,-4.02508 1,-16.13177 1,-21 0,-11 0,-22 0,-33 0,-12.28796 -3,-24.01751 -3,-36 0,-9.67987 -1,-19.97171 -1,-30 0,-6.52929 -1,-12.03238 -1,-18.59655 0,-16.26569 0.69766,-16.07785 14,-19.40345 6.64569,-1.66143 13.1908,-2.69262 19,-5 7.05609,-2.80261 14.02527,-5.75619 22,-7.41635 9.22534,-1.92053 18.17047,-3.94223 27.50519,-5.58365 6.80993,-1.19745 9.94812,-5.75699 15.49481,-8.5585 7.7467,-3.91266 10.94635,-1.5571 18,0 6.18359,1.36505 11.45886,5.36115 17.51788,6.5585 4.73096,0.93491 3.71411,5.53574 3.03046,8.59381 -1.21729,5.44495 -4.04059,7.58191 -4.04059,14.40619 0,8.84574 1.49225,17.50522 1.49225,26.50498 0,7.49963 -0.48212,14.89755 -0.48212,22.49502 0,10.53025 2.44056,20.61634 3.48212,31 0.73273,7.30439 0.21155,14.84027 1.56867,22 1.14923,6.06311 1.01013,12.2663 1.01013,18.44916 0,5.89527 -0.4527,7.013 -7.5788,8.08124 -1.64471,0.24652 -8.07367,3.76959 -12,4.4696 -1.35303,0.24121 -2.66669,0.66669 -4,1 -4.76123,1.19031 -14.67944,9.35883 -16,12 -2.03094,4.06195 -10.49164,7.24579 -14,9 -5.88348,2.94171 -10.24396,3.68659 -14,9 -2.30291,3.25775 -7.28601,7.47089 -10,9.48218 -3.22693,2.39142 -2.4248,1.8266 -6,3.03046 z"
- id="path29771"
- inkscape:connector-curvature="0"
- style="fill:#61847b;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 634.5788,428.63751 c -2.89502,2.14545 -12.42908,8.16956 -17.5788,9.45697 -4.04736,1.01184 -12.26459,5.1323 -16,7 -8.11096,4.05548 -19.06592,7.46417 -26,13 -5.28546,4.21964 -14.5282,6.07523 -21,7.41361 -6.26599,1.2958 -3,6.67846 -3,10.10153 0,8.82541 2,17.44516 2,26.48486 0,8.30362 -4,14.74402 -4,23 0,13.38282 1,26.06055 1,39.40857 0,12.98218 -2,25.63117 -2,38.59143 0,5.55609 3.89905,11.78675 2,16.46186 -3.2807,8.07666 8.97943,-0.71589 10,-1.46186 4.88971,-3.57403 9.6189,-6.4126 15,-10 7.03741,-4.69159 14.02185,-9.02185 20,-15 5.6286,-5.6286 15.64154,-9.3208 23,-13 0.99335,-0.49664 2,-0.96618 3,-1.44934 4.08069,-1.97137 6.88635,-4.84363 10,-8.08118 5.20337,-5.4104 5.07483,-8.36596 3.5788,-15.46948 -1.84266,-8.74939 0.4212,-19.63299 0.4212,-28.47217 0,-10.19323 0,-20.25705 0,-30.52783 0,-9.42224 -2.44153,-18.03311 -2.44153,-27.55594 0,-8.24005 0,-16.15744 0,-24.44406 0,-5.06576 -1.25012,-11.47964 2.02033,-15.45697 z"
- id="path30545"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient35246);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 549,464.09448 c 0.0751,-0.0379 -4.74365,-3.74362 -9,-8 -4.38879,-4.38879 -7.69751,-5.62417 -12.49738,-8 -3.34179,-1.65411 -11.58935,-0.95663 -15.50262,1 -3.58203,1.79102 -14.81674,2.27225 -5,-1 4.59973,-1.53323 10.56677,-3.582 15.45184,-6 6.48224,-3.20852 11.42127,-0.12954 15.54816,-6 2.05286,-2.92016 11.04437,-6.18249 14,-7 5.52039,-1.52688 10.92535,-1.72622 16.41382,-4 4.98987,-2.0672 10.63574,-3.62601 16.58618,-5 5.35553,-1.2366 9.00354,-4.40161 15,-5.59909 6.20679,-1.23953 11.27966,1.79669 17,3.03046 4.59839,0.99176 9.34094,3.33856 14,4.56863 2.24548,0.59287 4,4.24518 4,6.54303 0,1.40122 -14.23077,7.57236 -16,8.45697 -6.73914,3.36957 -13.52441,3.52442 -19,9 -5.97907,5.9791 -22.32416,7.16208 -30,11 -6.9588,3.47943 -16.25934,7 -24,7"
- id="path31351"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient35248);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 546.5177,620.59198 c -0.16675,0.0834 0,-10.18237 0,-18 0,-5.41608 0,-10.67755 0,-16 0,-8.53235 -1,-18.08319 -1,-27 0,-13.41791 4,-25.22095 4,-38.50262 0,-9.00983 -1,-17.7367 -1,-26.49735 0,-9.98672 -1.53711,-16.77991 -5,-25.52548 -1.83722,-4.63983 -5.31323,-8.07511 -8.40601,-12.47452 -1.78131,-2.53384 -9.26355,-3 -12.12182,-3 -5.51221,0 -11.29694,-2 -17.4722,-2 -4.26727,0 -8.55002,8.10007 -10,11 -2.41071,4.82144 -2,11.81162 -2,18 0,8.81332 2.23328,17.16641 4,26 1.96887,9.84433 3.46078,23.15683 1,32.99997 -2.48749,9.95007 -3,18.61713 -3,29 0,9.07257 3,18.36304 3,28 0,7.09119 2.01794,12 10,12 6.4006,0 12.02939,-2.65881 15.48233,2 3.45727,4.66467 14.9433,8.21155 19.5177,10.5025"
- id="path32119"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient35250);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 538,606.09448 c 4.18897,-2.06891 3.45612,-8.63165 2,-13 -1.46948,-4.40838 -1,-10.12189 -1,-15 0,-5.66595 1,-10.40271 1,-16 0,-5.81573 1,-10.2279 1,-15 0,-6.22845 1,-12.51337 1,-19 0,-5.56445 -1,-12.05591 -1,-18 0,-7.42245 -3.14734,-6.59784 -6.42627,-2 -1.14801,1.60974 -8.32855,1 -10.57373,1 -4.4433,0 -9.94922,-0.64972 -14,-2 -5.77506,-1.92502 -10.7717,0.45661 -8,6 2.32571,4.65143 1.6723,10.84546 4,15.54816 1.63919,3.31164 1,9.49505 1,13.45184 0,6.33331 0,12.66669 0,19 0,5.17682 0,10.35358 0,15.5304 0,5.70721 3.54254,11.38458 1,16.4696 -3.30441,6.60883 5.9411,4.47058 9,6 4.43915,2.21961 9.68054,2.26331 14,4 3.47156,1.39575 4.04437,0.55347 7,3 z"
- id="path32887"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient35252);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 535,498.09448 c 2.0235,0.81357 0.48523,-7.51474 -1,-9 -1.70148,-1.70151 -5.12787,-1 -7.50751,-1 -3.21441,0 -6.99237,1 -10.49249,1 -6.03949,0 -10.83929,-0.51782 -9,5 1.43735,4.31208 3.35785,6 9,6 3,0 6,0 9,0 3.54755,0 4.39362,-0.51523 8,-0.51523"
- id="path33655"
- inkscape:connector-curvature="0"
- style="fill:#ffb4bf;fill-opacity:0.59276003;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 508,487.09448 c 2.86563,0.46109 6.43652,-0.43652 9,-3 1.91528,-1.91531 3.64648,-2.83606 4.44171,-5.45443 0.95728,-3.15198 0.89508,-5.20881 -1.44171,-7.54557 -2.77887,-2.77884 -4.44208,-6 -8.40726,-6 -4.46203,0 -5.89551,1.30277 -8.59274,4 -1.39075,1.39075 -1.43262,7.13712 -0.49866,9 1.48963,2.97119 4.6261,4.44815 6.06092,7 0.82986,1.47586 5.6314,1 7.43774,1"
- id="path34415"
- inkscape:connector-curvature="0"
- style="fill:#4e6b64;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 511,483.09448 c 3.11499,0.0711 4,-2.68539 4,-6 0,-2.31396 1.45142,-5 -3.40726,-5 -2.3251,0 -4.04062,3.51264 -4.04062,5.53543 0,3.228 1.54837,5.46457 5.44788,5.46457"
- id="path35175"
- inkscape:connector-curvature="0"
- style="fill:#85aba1;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 498,455.1781 c -0.13486,8.22769 -0.0443,12.2384 0.4306,20.45291 0.79828,13.64649 1.06891,27.3472 2.30326,40.96463 1.78173,13.18439 3.65539,26.29309 3.40652,39.61865 -0.84634,11.96881 -1.53143,23.93079 -1.32086,35.93445 0.18326,5.8775 0.66257,11.74494 1.18323,17.60028 l -6.85422,3.56873 c -0.34164,-5.93146 -0.72894,-11.86463 -0.8992,-17.80371 -0.15695,-12.02393 0.37164,-24.02417 1.36493,-36.00623 0.31442,-13.30328 -1.36588,-26.47473 -3.18161,-39.63415 -1.07291,-13.60102 -1.74259,-27.24454 -2.53852,-40.86359 -0.55819,-8.05966 -0.6033,-8.94507 -1.89413,-16.91559 l 8,-6.91638 z"
- id="path29682"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 493,456.09448 c 4.35922,-5.67306 10.18231,-8.73459 16.99676,-11.31479 6.17164,-2.89618 12.68323,-4.83157 19.11298,-7.03283 7.27441,-2.64185 14.58301,-5.09592 21.98401,-7.362 8.57043,-2.47074 16.77551,-5.86295 24.93146,-9.4292 5.83996,-2.27951 11.74365,-4.38825 17.48865,-6.89679 l -6.28406,4.98718 c -5.66815,2.39813 -11.47174,4.43378 -17.2096,6.65674 -8.17584,3.58276 -16.43152,6.93164 -25.02911,9.39545 -7.46686,2.25476 -14.7699,4.91983 -22.11969,7.52182 -6.42011,2.19863 -12.91712,4.14263 -19.08151,7.03152 -3.71689,1.39163 -16.12345,7.90271 -3.34473,0.5303 0.85644,-0.49411 -3.42078,1.62622 -2.43442,1.69483 L 493,456.09448 z"
- id="path29684"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 642,428.09448 c -2.04382,6.54184 -2.66699,11.04407 -3.73505,17.81885 -1.34423,10.77637 -1.9931,21.65179 -1.7976,32.50888 0.68622,14.46338 1.4566,28.9184 1.81958,43.39511 -0.0602,12.06153 1.84283,23.987 2.79266,35.98432 0.32153,3.88891 0.71948,8.77173 0.92041,12.66809 l -7.05096,3.62475 c -0.11225,-3.93609 -0.41553,-8.85901 -0.62952,-12.79138 -0.73731,-12.09143 -2.44995,-24.12054 -2.33893,-36.25494 -0.2763,-14.43485 -0.93133,-28.84888 -2.09595,-43.24002 -0.32971,-10.89316 0.29725,-21.79001 1.68842,-32.60154 0.9129,-6.52703 1.83942,-13.15158 3.20319,-19.56463 L 642,428.09448 z"
- id="path29692"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 549.21051,628.15253 c -4.2273,-1.3985 -7.81848,-4.3678 -11.7948,-6.44184 -8.02045,-3.78546 -16.67114,-6.08398 -25.2005,-8.43444 -5.65167,-1.6836 -9.36081,-2.63282 -15.21521,-3.18177 l 4.2803,-4.47705 c 5.79602,0.82007 11.60868,1.61774 17.22782,3.32648 8.6076,2.35425 17.30743,4.70294 25.4165,8.48212 3.97772,2.02551 7.80817,5.16528 12.2649,5.65594 l -6.97901,5.07056 z"
- id="path29698"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 548,459.09448 c -0.35284,6.00974 4.74371,6.99048 5.13361,12.99741 0.70477,10.31457 0.22851,20.65274 0.0938,30.97922 0.005,11.36993 -0.62024,22.72339 -1.57178,34.04913 -1.18195,11.94751 -1.18396,23.93042 -0.43951,35.89831 0.69122,9.16639 0.48077,18.36853 0.94348,27.54242 0.53192,8.04077 1.82092,13.50568 2.49945,21.53351 0.058,1.35736 0.22735,2.70569 0.34094,4.05768 l -7.09857,3.70563 c -0.0862,-1.3747 -0.219,-2.74707 -0.25873,-4.12513 -0.5293,-8.06988 -1.78668,-13.56408 -2.34161,-21.63031 -0.40571,-9.19885 -0.26685,-18.41967 -0.80451,-27.61633 -0.48889,-12.03857 -0.50226,-24.08441 0.60993,-36.09601 0.92663,-11.28375 1.3836,-22.60705 1.44964,-33.92862 0.0994,-10.28009 0.57001,-20.58248 -0.22369,-30.84542 -0.46472,-5.89792 -3.46222,-6.719 -4.0188,-12.60511 L 548,459.09448 z"
- id="path29708"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 545.88281,484.07736 c 1.36408,-4.88272 0.8548,-9.76547 -0.1535,-14.63956 -3.81238,-8.97268 -12.5022,-12.44976 -21.07056,-15.71554 -10.44488,-6.0239 -30.88247,1.27685 -20.99344,-4.27912 -3.85476,2.47458 -5.75351,6.05716 -7.35031,10.1713 l -7.3284,3.46503 c 1.59201,-4.26053 3.44632,-8.1351 7.35578,-10.79651 11.9487,-6.96067 21.34591,-7.93616 34.50222,-2.72873 8.70856,3.38019 17.93018,6.86841 21.84308,16.07638 1.03925,4.78012 1.65046,9.67755 0.88147,14.53037 l -7.68634,3.91638 z"
- id="path29717"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 634.77625,433.56973 c -0.59754,-4.06387 -3.70563,-6.15906 -6.85785,-8.44586 -5.2981,-3.90744 -11.23481,-5.44882 -17.61811,-6.4921 C 603.37695,417.57794 593.91376,416.9361 587,418.09448 l 6.23633,-4.69229 c 6.96588,-1.00592 16.42969,-0.35357 23.3728,0.78641 6.61639,1.0235 12.62158,2.48025 18.07288,6.60467 3.3399,2.4068 6.54712,4.74747 7.78058,8.86008 l -7.68634,3.91638 z"
- id="path29719"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 550.4762,461.01083 c 5.24926,0.37168 10.37878,-0.78338 15.43469,-2.05572 9.20538,-2.76532 17.52344,-7.58704 26.52923,-10.88422 8.49891,-3.17456 17.25037,-5.93228 25.16706,-10.36349 6.18615,-4.55719 12.84405,-8.23303 19.60113,-11.82801 l -4.90222,3.86911 c -3.12085,1.79581 -6.13245,3.77918 -9.11609,5.79468 -11.20349,6.60984 -22.57788,12.46741 -35.18573,15.99445 -9.05011,3.47403 -17.48816,8.3941 -26.84418,11.05316 -5.30084,1.30618 -10.62445,2.38046 -16.11206,2.3638 l 5.42816,-3.94376 z"
- id="path29721"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 556.30237,624.09216 c -15.62933,10.25519 -1.15204,-1.14435 2.21094,-4.02777 4.79876,-4.406 9.87176,-8.45813 15.16308,-12.25085 6.07733,-4.29767 12.19342,-8.54712 18.42841,-12.61292 8.44708,-5.71655 17.64068,-10.16015 26.52692,-15.12744 7.14367,-4.17981 15.03295,-6.79455 22.76452,-9.65283 0.49304,-0.16327 0.98602,-0.32648 1.47907,-0.48975 l -6.22437,4.83454 c -0.47821,0.1593 -0.95648,0.3186 -1.43469,0.4779 -7.71057,2.82813 -15.53674,5.4917 -22.63068,9.70661 -22.86804,12.71722 0.79499,-1.19068 -13.12854,7.39172 -6.2724,4.03284 -12.40472,8.28345 -18.48358,12.60162 -5.26385,3.73194 -10.28955,7.75861 -15.06036,12.10749 -5.32703,4.5899 -9.85419,9.43731 -16.58973,12.11224 l 6.97901,-5.07056 z"
- id="path29723"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 505.25861,503.66565 c -0.7009,4.62793 -0.53107,9.31702 -0.284,13.97241 0.5842,5.94275 1.43524,11.8606 2.1727,17.78796 1.08362,6.77686 1.42895,13.63074 1.57706,20.48237 0.16906,7.23968 0.28241,14.48126 0.24868,21.72283 -0.0319,5.8407 -0.63897,11.65204 -0.95584,17.47828 -0.0311,0.58239 -0.0522,1.16522 -0.0783,1.7478 l -2.70718,1.36163 c 0.0376,-0.58935 0.0696,-1.17908 0.11276,-1.76806 0.42362,-5.85218 1.09995,-11.69031 1.15488,-17.56379 0.0387,-7.24548 -0.0548,-14.49298 -0.19562,-21.73718 -0.2179,-6.82489 -0.57083,-13.65826 -1.6571,-20.40887 -0.82456,-5.9217 -1.62455,-11.85492 -2.18589,-17.80573 -0.2316,-4.58276 -0.33005,-9.16107 -0.19125,-13.74661 l 2.98913,-1.52304 z"
- id="path29735"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 508.17975,594.83185 c 2.85043,0.34479 5.64032,1.36371 8.41846,2.13824 4.24444,1.39118 8.34613,3.17877 12.53637,4.71686 3.94672,1.37793 7.84833,2.87995 11.80396,4.23194 0.4754,0.16168 0.95233,0.31866 1.42846,0.47796 l -2.55883,1.73004 c -0.0842,-0.0301 -1.3523,-0.50818 -1.39527,-0.4992 -3.94598,-1.34546 -7.80139,-2.9339 -11.75073,-4.2713 -4.17645,-1.55475 -8.28741,-3.29468 -12.51446,-4.70545 -2.8288,-0.80462 -5.7219,-1.87787 -8.68201,-1.84723 l 2.71405,-1.97186 z"
- id="path29739"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 539.64514,607.66272 c -0.006,-2.43085 -0.37494,-4.85223 -0.67694,-7.26239 -0.70074,-4.30597 -0.84436,-8.66528 -0.98108,-13.01666 -0.1156,-4.63178 -0.15326,-9.26514 -0.17047,-13.89826 -0.056,-5.86541 0.092,-11.7268 0.26611,-17.58868 0.0303,-6.68207 0.59522,-13.34119 0.89423,-20.01257 0.46161,-6.61682 0.6463,-13.24482 0.7511,-19.87525 0.0408,-4.16818 -0.18366,-8.3378 -0.77728,-12.46161 l 2.72833,-1.44998 c 0.53686,4.18598 0.69098,8.40564 0.58148,12.62644 -0.12744,6.63897 -0.33081,13.27764 -0.79059,19.90307 -0.29065,6.67236 -0.80798,13.33521 -0.90796,20.0152 -0.1571,5.82989 -0.18689,11.65637 -0.0881,17.4881 0.023,4.63354 0.035,9.26758 0.15173,13.8999 0.12158,4.33057 0.32477,8.6701 1.05048,12.94855 0.31903,2.38696 0.62384,4.77893 0.95813,7.16113 l -2.98914,1.52301 z"
- id="path29741"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 506.15945,503.91809 c 1.90223,1.30429 4.11978,1.9964 6.34134,2.57825 5.12714,1.08432 10.38147,1.33609 15.59833,0.98446 4.46051,-0.30749 8.67493,-1.82778 12.83301,-3.36138 l -2.43634,1.90045 c -4.12567,1.46917 -8.30634,2.89499 -12.71442,3.17154 -5.26324,0.36578 -10.57007,0.0576 -15.74759,-0.99015 -2.27533,-0.56823 -4.50595,-1.31665 -6.58841,-2.31128 l 2.71408,-1.97189 z"
- id="path29743"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 505.65436,523.11096 c 2.20019,0.12232 4.36307,0.65216 6.53039,1.03558 4.93073,0.73139 9.92164,0.91498 14.89747,0.76075 4.31939,-0.18128 8.57733,-0.89276 12.67901,-2.24689 1.59326,-0.66876 3.26117,-1.08063 4.93274,-1.47827 l -2.57178,1.93384 c -1.6261,0.37555 -3.23736,0.78137 -4.78527,1.42865 -4.09766,1.25885 -8.32019,1.97082 -12.61487,2.09308 -5.00629,0.13427 -10.02844,-0.0513 -14.99118,-0.76667 -2.24536,-0.35907 -4.50562,-0.88385 -6.79056,-0.78814 l 2.71405,-1.97193 z"
- id="path29745"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 508.17975,540.28357 c 1.63162,0.82745 3.42679,1.2077 5.22009,1.52264 3.08966,0.23108 6.20178,0.10077 9.30005,0.076 3.31232,0.18317 6.5611,-0.29358 9.78638,-0.99396 3.21209,-0.6809 6.30639,-1.82165 9.53021,-2.42242 0.62915,-0.11639 1.26312,0.0115 1.89282,0.0172 l -2.61212,1.8664 c -0.59912,8.5e-4 -1.20727,-0.13074 -1.80573,0.003 -3.18743,0.62646 -6.26098,1.72912 -9.44482,2.39465 -3.25885,0.6897 -6.5426,1.12842 -9.88281,0.98017 -3.10095,0.01 -6.21356,0.1231 -9.30588,-0.12549 -1.83716,-0.30738 -3.63184,-0.81482 -5.39224,-1.34583 l 2.71405,-1.97186 z"
- id="path29747"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 507.67468,554.93079 c 1.94287,0.17187 3.86609,0.50933 5.81061,0.69201 4.22534,0.16913 8.44159,0.48395 12.66589,0.67621 4.64325,0.18854 9.29126,0.26825 13.92609,-0.10541 1.20264,-0.0494 2.37463,-0.31506 3.55151,-0.53601 l -2.5285,1.91266 c -1.14526,0.16461 -2.28827,0.35754 -3.44635,0.41425 -4.63641,0.31805 -9.27466,0.22192 -13.91699,0.0542 -4.24097,-0.1662 -8.4751,-0.48145 -12.7161,-0.6521 -2.02328,-0.13581 -4.03305,-0.50415 -6.06024,-0.48389 l 2.71408,-1.97192 z"
- id="path29751"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 509.69498,567.05261 c 1.85965,0.10431 3.69857,0.43994 5.55069,0.64722 3.47204,0.16479 6.8977,0.60785 10.29791,1.31482 4.25568,0.84112 8.53412,1.59851 12.84894,2.06103 2.11712,0.20441 4.24194,0.29853 6.36615,0.38898 l -2.6286,1.87811 c -2.05848,-0.19055 -4.12091,-0.33447 -6.17926,-0.53052 -4.32355,-0.4765 -8.61481,-1.23309 -12.88013,-2.07879 -3.42499,-0.66925 -6.8811,-1.04456 -10.36633,-1.2265 -1.90039,-0.21656 -3.806,-0.57019 -5.72342,-0.48242 l 2.71405,-1.97193 z"
- id="path29753"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 509.18991,581.69983 c 1.93207,-0.0142 3.83292,0.16437 5.73605,0.49109 4.40046,0.86017 8.69635,2.1867 13.00373,3.41998 3.76123,1.18652 7.53845,2.30823 11.29883,3.49793 1.60742,0.54626 3.19519,1.14605 4.7904,1.72601 l -2.56604,1.73578 c -1.58466,-0.59833 -3.16406,-1.21167 -4.77911,-1.72541 -3.73023,-1.20172 -7.46454,-2.37817 -11.20325,-3.55389 -4.30267,-1.26862 -8.62421,-2.5412 -13.04102,-3.3465 -1.97894,-0.2799 -3.95566,-0.41449 -5.95367,-0.27313 l 2.71408,-1.97186 z"
- id="path29761"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 510.46002,464.47372 c -4.37897,1.15763 -1.7265,0.45246 -2.50491,0.75693 -2.46185,1.7305 -4.00613,4.11301 -5.04019,6.87943 -0.79764,3.72924 0.11383,6.93963 2.37088,9.93161 3.02966,3.39145 6.58728,4.38247 10.85183,3.06033 -0.15405,0.43555 3.38452,-1.64114 5.13776,-6.36362 0.9212,-3.72442 -0.71167,-6.68194 -3.0473,-9.45126 -1.23505,-1.32172 -2.83637,-1.8699 -4.50562,-2.40057 l 2.59925,-1.78043 c 1.70214,0.57867 3.33038,1.17709 4.56048,2.56992 2.3039,2.846 4.0943,5.97512 3.15607,9.76004 -1.72492,4.88317 -5.52063,7.59017 -10.31037,9.53762 -4.37283,1.22791 -7.97186,0.0243 -11.03622,-3.37811 -2.30081,-3.07516 -3.30991,-6.28931 -2.60187,-10.13937 0.9896,-2.82758 2.5044,-5.28379 4.97415,-7.08393 2.56775,-1.57876 5.03434,-3.55243 8.11011,-3.87048 l -2.71405,1.97189 z"
- id="path29763"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 508.28906,490.53366 c -0.0542,1.59458 -0.134,3.1929 -0.20907,4.78098 0.84707,2.21286 2.96026,3.2405 5.03109,4.14285 3.98902,1.41043 8.26807,1.64755 12.46283,1.4968 3.33692,0.61279 12.64246,-4.37247 8.16517,-2.01438 1.31701,-1.8941 1.2044,-4.2514 1.15387,-6.48633 0.0811,-2.14221 -0.32563,-3.51077 -1.69935,-4.83758 -2.09845,0.44421 -4.17138,1.20795 -6.28064,1.73434 -3.37042,0.83277 -6.85186,0.97605 -10.30584,1.11396 -3.30365,-0.0278 -6.58261,-0.55237 -9.82892,-1.12836 -3.64349,0.41107 0.47818,-3.20178 -0.51471,0.86264 l -2.87427,1.48013 c 0.34339,-2.93808 2.8385,-4.96582 5.96457,-4.1466 3.20719,0.60147 6.45303,1.15805 9.72617,1.12112 3.45135,-0.14359 6.92608,-0.31622 10.28711,-1.17981 2.16333,-0.56747 4.32153,-1.55646 6.51971,-1.70038 1.53101,1.44446 1.85498,3.02106 1.82275,5.22867 0.0718,2.2959 0.14332,4.68445 -1.01031,6.73196 -4.23621,2.64768 -8.30536,4.91654 -13.47583,4.94953 -4.27008,0.18088 -8.62207,-0.10996 -12.69693,-1.48542 -2.20398,-0.96862 -4.38498,-2.04498 -5.29764,-4.40155 0.0589,-1.57339 0.20502,-3.16021 0.0711,-4.73953 l 2.98913,-1.52304 z"
- id="path29765"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 511.83264,470.35968 c 1.84894,-0.13138 2.86127,0.91983 4.06091,2.13104 1.31318,2.28073 1.67078,4.58298 0.91846,7.0726 -1.93512,3.00824 -4.93735,4.8176 -8.36047,3.56342 -2.60526,-1.74716 -3.13101,-4.11289 -2.48856,-7.01352 1.10251,-2.65848 3.56485,-3.41806 5.98331,-4.57806 l -1.8136,1.39084 c -0.81805,0.63562 0.22324,-1.19889 -2.14783,2.24282 -0.68051,2.78824 -0.19354,5.06675 2.34867,6.72708 4.21163,1.36362 1.69577,1.37574 4.42465,-1.39718 0.81695,-2.40582 0.46863,-4.62833 -0.81684,-6.85205 -1.22039,-1.19516 -2.20758,-2.12162 -4.04733,-1.87851 l 1.93863,-1.40848 z"
- id="path29769"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer10"
- display="none"
- style="display:none">
- <g
- id="g21562"
- transform="matrix(0.890561,0,0,0.890561,-1078.012,-547.8151)">
- <path
- d="m 539.28156,203.28651 c -1.14417,0.11324 -6.612,0.0386 -9.68848,-0.34671 -3.44617,-0.43165 -7.58386,-0.36052 -10.3805,1.03875 -2.62268,1.31224 -4.13232,3.08435 -6.22833,6.22831 -1.89178,2.8377 -2.76813,6.82174 -2.76813,10.38051 0,4.53996 3.8039,5.3886 6.92035,7.90402 2.18988,1.76757 3.6482,3.29387 5.53626,4.55259 2.79571,1.8638 9.97034,1.69335 13.14868,1.69335 4.2804,0 8.88141,1.04456 11.76459,-2.38539 2.51074,-2.98694 5.5979,-2.63679 6.92035,-7.26744 1.10406,-3.86599 -2.22785,-7.0356 -4.15223,-9.34137 -2.1604,-2.58859 -3.94189,-6.16788 -4.84423,-9.39645 -0.68567,-2.4534 -7.92994,-2.20935 -8.99646,-1.6761"
- id="path3386"
- inkscape:connector-curvature="0"
- style="fill:url(#radialGradient21589);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 454.85342,269.02976 c -0.32901,-1.17719 -8.05017,-2.07612 -11.76458,-2.07612 -4.50095,0 -5.95606,0.90195 -9.68848,2.76816 -2.82034,1.41016 -5.64926,3.56018 -7.93591,5.8981 -2.69889,2.7594 -2.41141,4.64847 -1.75257,7.94257 0.69046,3.45239 6.55353,4.51022 9.68848,4.84424 3.04547,0.32449 6.5993,2.72827 9.68848,3.11194 3.2911,0.40875 4.77557,-1.72788 8.99643,-1.72788 3.82589,0 6.22653,-0.936 8.58361,-3.46017 2.71711,-2.90973 2.48893,-4.58194 2.48893,-8.99643 0,-4.89771 -4.58683,-6.16815 -8.30439,-8.30442 -0.67208,-0.3862 -3.2117,-1.60583 -4.15222,-2.07611"
- id="path3388"
- inkscape:connector-curvature="0"
- style="fill:url(#radialGradient21591);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 483.22681,231.33046 c -3.7598,-0.65524 -5.11759,0.46323 -7.61237,3.78963 -1.84174,2.45562 -3.43014,3.4254 -5.53628,5.86319 -1.13007,1.30803 -2.43476,5.07982 -2.76813,6.24595 -1.22595,4.28827 2.01684,6.97156 2.76813,10.72799 1.03744,5.18713 -0.849,4.73956 3.46017,7.61236 2.85828,1.90552 3.44449,4.37244 6.2283,6.2283 2.57541,1.71695 6.86869,2.3963 8.99646,3.46018 2.87723,1.43863 5.51706,-1.02844 7.61237,-2.07611 3.30704,-1.65351 4.91226,-2.41968 8.01504,-4.15219 2.03977,-1.13898 3.21292,-4.77387 3.74955,-6.92035 0.60647,-2.42597 -1.07694,-5.42157 -2.07612,-6.92034 -1.13452,-1.70179 -1.10562,-5.63688 -1.10562,-8.30441 0,-4.12154 0.7179,-6.44077 -3.40689,-7.61237 -4.01648,-1.14084 -7.23437,-3.80175 -11.40426,-4.84423 -2.44614,-0.61155 -3.44998,-2.62629 -6.92035,-3.0976 z"
- id="path4970"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21593);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 488.76309,275.9501 c 0.39527,0 0.39527,0 0,0 -3.51358,0 6.76499,-2.20321 9.68848,-4.15222 2.88199,-1.92132 3.20849,-1.8616 2.76813,-5.53625 -0.15619,-1.30344 -5.15915,-3.28812 -6.92035,-4.15222 -3.59339,-1.763 -3.04038,-4.1522 -7.61237,-4.1522 -3.10702,0 -6.66751,3.33378 -8.30441,4.1522 -3.37284,1.68643 -2.85947,3.45336 -2.07611,6.92034 0.24249,1.07312 6.11929,3.45536 7.61239,4.1522 2.56229,1.19589 1.42871,0.20651 4.84424,2.76815 z"
- id="path5740"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21595);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 505.11905,264.75717 c -3.2132,0.61505 -4.81717,-1.23923 -6.69128,-3.01105 -1.61091,-1.52298 -3.29664,-2.89813 -4.80665,-3.88491 -2.35556,-1.53934 -4.19186,-2.25196 -5.38488,-3.94381 -1.41227,-2.00277 -0.90226,-5.25685 -1.91092,-6.92665 -1.07379,-1.77766 -0.63697,-5.32107 -0.63697,-7.52636 0,-3.67237 0.76761,-3.3441 4.45881,-3.3441 3.87222,0 6.39471,0.44358 8.9176,2.47055 2.13113,1.7122 4.09448,3.41932 5.09576,5.08902 1.9787,3.29956 1.91092,9.68385 1.91092,13.24861 0,3.90613 0.5123,4.12414 -0.95239,7.8287 z"
- id="path6508"
- inkscape:connector-curvature="0"
- style="fill:#4a87bd;fill-opacity:0.57013604;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 451.68378,236.17123 c -1.6203,1.44159 -5.7117,3.78528 -8.30442,5.53627 -2.35513,1.59055 -6.26324,1.08989 -8.99642,1.08989 -2.94223,0 -5.09742,-1.74174 -7.29719,-3.166 -2.57537,-1.66743 -4.98657,-3.18008 -7.23556,-5.53627 -1.1261,-1.17979 -5.01041,-2.1114 -6.2283,-2.37008 -3.6684,-0.77922 -6.46207,-3.87789 -10.3805,-4.55025 -2.39147,-0.41036 -6.66857,-1.95023 -8.30441,-2.76814 -2.2056,-1.1028 -3.46017,-2.17939 -3.46017,-4.84424 0,-2.33252 2.16259,-4.73245 3.8114,-5.53627 2.29645,-1.11952 3.37958,-4.42284 5.18503,-6.2283 1.89212,-1.89212 1.63555,-3.99424 4.15222,-5.883 1.64245,-1.23268 3.4205,-4.40863 5.142,-6.24594 1.40316,-1.49757 6.0567,-5.05077 8.00665,-5.46245 2.37826,-0.50212 3.25281,4.44274 5.90478,4.44274 3.0325,0 5.26581,2.13559 7.93589,3.85878 0.47586,0.30709 0.9227,0.65698 1.38406,0.98545 2.01996,1.43821 5.22757,3.99787 6.92035,4.84424 1.78867,0.89433 5.22406,4.53201 6.2283,5.53627 1.77368,1.77367 3.40656,3.35294 4.15222,4.84424 0.86322,1.72647 3.07416,2.50598 4.15219,4.15221 1.13819,1.73806 4.76047,1.6454 6.22831,3.76963 1.59866,2.31357 0.70865,3.70781 -0.69202,5.91884 -0.86325,1.36269 -2.55231,3.72055 -3.46018,5.53627 -0.54776,1.09552 -5.89346,3.15216 -7.29754,4.44367"
- id="path6512"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21597);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 536.51343,301.55536 c -1.27893,1.17642 -8.65949,-2.45542 -9.97821,-2.76813 -3.13819,-0.74414 -6.12488,-3.08642 -9.39874,-4.4295 -0.3642,-0.14942 -0.73719,-0.27649 -1.10584,-0.41474 -2.78058,-1.04284 -3.51587,-2.52182 -5.12247,-4.84423 -0.73275,-1.05927 0.066,-5.15247 0.29609,-6.22831 0.93991,-4.39395 2.44088,-2.70584 3.85611,-5.53628 1.03088,-2.0618 3.27508,-2.88062 3.80932,-4.55335 0.62268,-1.9494 4.77899,-3.05902 6.8551,-4.44311 1.79218,-1.38407 2.48426,-0.31934 4.56037,1.38409 2.07605,1.38407 5.78589,2.93332 6.92029,4.15219 1.79132,1.92466 2.24102,3.45124 3.80896,5.53629 1.70556,2.26797 4.54712,1.74377 5.87951,3.87509 1.98291,3.17175 5.53632,1.82437 5.53632,6.5054 0,1.34748 -0.57813,3.33646 -2.76813,4.84424 -1.6571,1.14087 -3.6604,3.14749 -5.24078,4.15222 -2.41058,1.53247 -4.18628,0.43058 -6.52381,2.76813 -0.21508,0.21506 -3.81048,0.95081 -4.15222,1.03565"
- id="path6514"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21599);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 484.61087,233.73602 c -1.70105,-1.77022 -4.49179,-1.94385 -5.92899,-1.38408 -1.71377,0.6675 0.17881,2.76815 -3.75949,2.76815 -2.16098,0 -2.45691,5.83023 -3.86908,6.92035 -1.68545,1.30104 -2.03338,4.6865 -1.66717,6.92033 0.66867,4.07877 -2.05133,4.46864 0.69201,8.02003 1.92041,2.48606 -0.30841,6.07489 0.97516,7.89676 0.0279,0.0396 1.25772,-2.25454 3.86908,-1.38409 2.00794,0.66934 3.50846,-1.19531 5.53629,-2.76813 3.00738,-2.33258 6.04858,-4.40707 6.92035,-5.53627 1.31036,-1.69732 -0.50131,-6.18557 -1.06858,-6.92035 -0.6311,-0.81746 -1.00753,-6.98497 -1.00753,-7.61236 0,-2.48783 0.10217,-4.53769 -0.69205,-6.92034 z"
- id="path8052"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21601);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 468.59528,242.62471 c 0.61132,3.14207 1.19793,6.29893 1.86779,9.43459 0.22724,3.77815 0.84073,7.50882 1.11322,11.28302 1.83722,4.04168 7.55143,4.90741 11.4766,6.82543 4.50464,2.82837 5.84198,6.715 11.21377,4.35498 1.72583,-0.89294 5.09806,-3.16598 13.8476,-5.81301 -2.10919,2.88006 -0.81305,-2.74952 -1.357,-8.21119 -0.0701,-5.26841 -0.84161,-10.49858 -1.73391,-15.6779 -1.0821,-4.15925 -3.67511,-4.79474 -7.33811,-5.92755 -4.87924,-2.19115 -9.45633,-4.94589 -14.67782,-6.31325 -6.73182,0.21466 -6.06882,0.75522 -10.96442,8.12442 -0.66275,0.85723 -1.15357,1.82659 -1.72897,2.73772 l -4.37784,1.95107 c 0.62649,-0.94082 1.13077,-1.97242 1.8847,-2.83017 4.1453,-6.26038 11.17578,-13.45495 19.15836,-12.74108 5.15256,1.61409 9.68497,4.54964 14.75305,6.31003 3.87677,1.11932 6.58209,2.03561 7.45651,6.46336 0.79438,5.1987 1.68296,10.40543 1.87629,15.66773 0.72894,7.12665 1.84823,9.98224 -6.57947,13.26089 -11.85056,3.72852 -2.09256,0.0281 -13.95227,5.98044 -5.56183,2.24185 -6.79093,-1.98196 -11.38376,-4.77844 -4.16706,-2.04166 -10.10501,-2.71732 -11.92984,-7.13818 -0.29318,-3.75672 -0.86225,-7.46647 -1.07459,-11.23026 -0.65536,-3.12013 -1.07071,-6.38109 -2.26996,-9.32765 l 4.72007,-2.405 z"
- id="path3249"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 465.60232,224.4098 c 8.62806,-2.1071 -2.45343,5.50655 -4.81716,8.00156 -4.60334,4.4532 -10.28458,7.72264 -16.21393,10.07246 -4.3797,2.45507 -7.64993,4.42971 -12.40424,2.03251 -6.35824,-2.74848 -11.73102,-7.18296 -17.85965,-10.35642 -5.64294,-3.02945 -11.57068,-5.63837 -17.62363,-7.7634 -7.04315,-2.78808 -7.9739,-2.01512 -3.21786,-7.61161 4.40597,-6.64257 9.14291,-13.02157 14,-19.33947 5.29694,-6.13575 10.30405,-12.90852 18.40903,-7.77517 8.79217,6.38936 17.56594,12.80662 26.42767,19.0952 6.35507,4.39817 12.22366,9.4528 18.37598,14.13227 l -3.86057,2.40006 c -5.57455,-5.23633 -11.51712,-10.05872 -17.77865,-14.4594 -8.8144,-6.43527 -17.95154,-12.38926 -26.72803,-18.86793 -8.5275,-4.31734 -1.83646,-4.52248 -10.78772,3.7374 -5.00958,6.13485 -9.47742,12.6725 -13.85578,19.26779 -4.02859,5.13751 -4.01913,4.24205 2.85849,6.83008 6.0326,2.25247 11.96387,4.86602 17.69977,7.78179 5.89453,3.48401 11.40943,7.63665 17.68958,10.43966 4.84765,1.6912 8.1196,-0.11974 12.44348,-2.51429 7.18231,-2.92677 -0.21341,0.92041 7.97311,-5.26509 4.55838,-4.49162 6.37409,-8.93605 12.36188,-11.58433 l -3.09177,1.74634 z"
- id="path3255"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 508.04071,289.00311 c -1.36322,-4.69955 2.08621,-8.86612 5.18597,-12.17938 3.59754,-3.23294 6.74054,-6.83145 10.61426,-9.75073 7.55042,-5.84503 10.00733,-2.36371 15.82117,3.02209 2.92328,4.46839 7.25806,7.37049 11.15332,10.87619 3.15192,2.53394 6.50152,4.85569 8.68841,7.98072 -2.71032,3.95611 -18.22338,11.11731 -13.36724,8.4573 -7.06745,6.39365 -11.38544,6.16229 -19.67408,3.01615 -6.28192,-2.81891 -13.23632,-6.83887 -19.61544,-9.33097 l 4.06109,-2.68777 c 6.15311,2.6889 12.93973,6.84094 19.04379,9.66733 11.141,3.85473 7.41034,3.94903 11.91571,1.00119 11.1894,-6.6702 8.83686,-4.05115 13.05457,-7.8512 -1.8045,-2.95093 -5.21625,-5.30432 -8.13776,-7.76041 -3.94531,-3.45996 -8.12616,-6.51507 -11.14782,-10.90615 -4.60413,-4.38739 -11.85144,-6.22364 -7.30371,-7.25846 -4.03919,2.86863 -7.13575,6.6312 -10.92829,9.7985 -3.01196,3.08316 -6.71716,7.09906 -4.64386,11.50061 l -4.72009,2.40499 z"
- id="path3259"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 464.79932,275.80237 c -4.77335,-1.98282 -9.13981,-4.85138 -13.92215,-6.88391 -6.56153,-2.7154 -14.98377,-0.0246 -20.95993,3.81112 -5.95285,5.34229 -7.13309,11.18839 -0.24363,15.89295 7.11307,3.84506 15.03171,4.92331 22.95975,3.73788 3.69171,-0.25345 10.52756,-4.0303 9.08197,-14.18573 -1.48813,-5.84366 -6.06873,-6.10461 -11.53821,-7.37494 l 1.38852,-0.98098 c 6.28443,0.33749 12.54373,-0.82459 14.01666,6.276 1.67344,10.3284 -6.68505,16.84262 -16.35852,18.8389 -8.03601,1.18734 -16.13318,0.0274 -23.32501,-3.88587 -7.39493,-4.72821 -6.07937,-11.05246 -0.043,-16.69864 8.78015,-5.58255 17.95035,-10.93127 28.69489,-7.91211 4.92465,1.90024 9.28045,5.42157 14.53445,6.25159 l -4.28577,3.11374 z"
- id="path3275"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 560.13831,226.56453 c -6.29218,8.24681 -17.22181,11.08891 -27.08576,12.44826 -8.62482,0.31726 -17.97644,-1.05195 -23.52713,-8.26847 -4.81985,-11.56198 1.8328,-21.82528 11.83762,-27.3582 9.40295,-2.7104 18.10748,-2.97889 26.17431,2.73748 6.33948,5.26245 9.58393,12.86923 9.14801,21.00206 -10.93524,10.29324 -29.68744,10.72283 -44.20599,13.20539 l 0.0855,-0.0644 c 12.93665,-2.21226 30.30133,-2.65521 40.2334,-11.24078 0.53906,-8.03491 -2.84082,-15.31627 -9.02643,-20.51836 -7.87878,-5.46492 -16.55005,-5.58149 -25.63355,-2.5412 -0.91345,1.14922 -10.02819,13.77806 -4.70611,22.49375 5.29205,7.26594 14.59399,8.40057 23.04516,8.00206 9.88202,-1.38211 13.8866,-1.55193 18.94092,-7.49263 l 4.72003,-2.405 z"
- id="path3279"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 465.48123,248.85265 c -5.32446,-3.22234 -10.85464,-6.3418 -16.53476,-9.13183 l 3.64316,-2.37695 c 5.72138,3.23484 10.09344,5.93144 16.10446,8.15672 l -3.21286,3.35206 z"
- id="path3283"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 507.82162,267.06754 c 4.02735,0.49117 7.53171,3.18109 11.13297,5.02111 l -3.95935,2.56479 c -3.52704,-2.12167 -7.16397,-4.83941 -11.45938,-4.47213 l 4.28576,-3.11377 z"
- id="path3285"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 469.00513,270.62616 c -0.22681,0.0743 -1.45597,2.71768 -1.28058,2.55579 l -3.39737,-1.04233 c 3.32407,-1.94559 6.47229,-4.51172 10.1882,-5.51688 l -5.51025,4.00342 z"
- id="path3287"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 510.8429,238.51741 c 0.45077,-0.15002 5.33288,-5.40183 4.90948,-5.18625 l -0.54748,3.85295 c -3.17457,1.94023 -6.16318,4.63114 -9.87226,5.33672 l 5.51026,-4.00342 z"
- id="path3289"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 471.07394,265.90637 c -5.0029,3.44806 10.84024,-5.50238 16.12009,-8.50928 1.8176,-3.41661 7.29505,-0.0632 13.63318,5.4311 2.44955,2.30032 5.14191,3.96398 8.22958,5.17327 l -2.8457,1.94074 c -3.0466,-1.3248 -5.7959,-2.99808 -8.19604,-5.36417 -7.83585,-6.98867 -7.32544,-9.84945 -13.39292,-5.10843 -5.42859,3.08503 -10.79971,6.37302 -16.60943,8.66089 l 3.06124,-2.22412 z"
- id="path3297"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 488.07105,256.88834 c -3.00354,-11.07962 -2.47736,-16.27713 -3.46018,-27.99656 l 0.69205,-0.79954 c 0.7435,11.2892 1.0343,19.29513 6.45715,28.981 l -3.68903,-0.1849 z"
- id="path3309"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 446.54901,280.79434 c -3.16635,6.00711 -5.26367,12.44772 -7.04755,18.97641 l -1.62509,0.76972 c 1.80789,-5.63929 3.68546,-11.34833 3.90464,-17.31672 l 4.768,-2.42941 z"
- id="path3363"
- inkscape:connector-curvature="0"
- style="fill:#72610c;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 535.74518,219.39265 c -2.85224,7.72808 -4.6037,15.71215 -6.02881,23.77842 l -2.58978,1.63749 c 2.34906,-7.18001 3.95513,-14.96213 2.63336,-22.5121 l 5.98523,-2.90381 z"
- id="path3365"
- inkscape:connector-curvature="0"
- style="fill:#72610c;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 448.73019,238.93056 c -3.91592,1.49482 -7.46545,2.58984 -11.44711,0.76936 -5.12308,-3.60487 -8.84598,-8.89984 -12.83624,-13.68218 -4.00272,-5.5379 -9.14145,-9.54408 -15.28061,-12.41351 -2.75476,-1.21788 -5.70789,-1.8679 -8.63178,-2.5309 l 1.75382,-1.21501 c 2.92578,0.74551 5.90665,1.37361 8.70279,2.53436 6.14419,2.93074 11.18438,7.06704 15.32837,12.49955 3.9754,4.76139 7.61001,10.10696 12.80377,13.60015 4.15854,1.68802 7.75171,0.38916 11.7716,-1.13452 l -2.16461,1.5727 z"
- id="path3373"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.48868797;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 408.44467,200.45001 c 5.34302,0.28324 10.15,3.29678 14.44181,6.27391 6.78103,3.69873 12.16513,8.79302 17.31851,14.4399 4.84686,4.85904 8.22613,10.87038 12.43573,16.22146 0.82528,1.14901 2.00924,1.53443 3.20541,2.08938 l -2.02637,1.37281 c -1.17483,-0.6316 -2.35168,-1.0968 -3.13528,-2.29052 -4.19705,-5.33467 -7.20978,-11.59432 -12.09543,-16.41002 -5.23974,-5.44074 -10.77191,-10.56336 -17.35504,-14.36708 -4.45505,-2.97075 -9.41083,-6.05187 -14.95398,-5.75714 l 2.16464,-1.5727 z"
- id="path3375"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.48868797;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 542.28449,298.23642 c -6.55494,0.13199 -11.30646,-3.71365 -15.98176,-7.8223 -4.13006,-4.27798 -8.57952,-7.92578 -13.75085,-10.83657 l 1.02539,-0.66947 c 5.1388,2.98352 9.6236,6.56211 13.73486,10.87873 4.72516,4.18218 9.59736,8.01669 16.27112,7.50598 l -1.29876,0.94363 z"
- id="path3381"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.32126698;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 520.43872,270.60556 c 6.03369,3.35562 11.06195,8.62033 16.26691,13.18002 4.50152,3.77201 6.55871,9.70395 11.90149,12.4217 l -1.22138,0.81127 c -5.24444,-2.84402 -7.18011,-8.83319 -11.64355,-12.63452 -5.12018,-4.58798 -10.26899,-10.12378 -16.60224,-12.83484 l 1.29877,-0.94363 z"
- id="path3383"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-opacity:0.32126698;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer11"
- display="none"
- style="display:none">
- <g
- id="g21644"
- transform="matrix(0.794248,0,0,0.794248,-1029.241,-371.3843)">
- <path
- d="m 398.39755,588.83453 c -2.70309,-0.43164 -9.0423,0.42585 -13.38559,0.42585 -4.87649,0 -7.52157,-2.42347 -9.86307,-5.96186 -2.9299,-4.42749 -0.98629,-10.94525 -0.98629,-15.89825 0,-5.96185 0,-11.92365 0,-17.8855 0,-9.00824 1.55456,-16.7782 2.95889,-25.26685 1.09134,-6.5968 3.94525,-10.15313 3.94525,-17.45971 0,-6.32202 1.12408,-11.3465 2.95889,-16.89188 1.63742,-4.94873 4.85282,-7.23382 8.87677,-9.9364 3.84693,-2.58371 7.52799,-2.23386 10.84937,-5.39404 4.11279,-3.91321 14.34781,-7.35626 19.72613,-9.93643 7.70566,-3.69663 13.98145,-6.66517 22.12143,-8.51691 6.853,-1.55896 12.99948,-4.97894 19.72614,-6.95548 6.87698,-2.02072 12.79907,-6.24826 19.3034,-9.36862 5.04633,-2.42087 10.19263,-2.43716 15.21732,-4.9682 3.97314,-2.00134 7.03866,-2.35565 10.42666,-0.56778 4.13096,2.17993 7.86979,2.20648 11.83567,5.53598 3.56982,2.99698 6.58649,4.34265 10.84936,6.3877 0.98633,0.47317 1.9726,0.94632 2.95893,1.41949 4.69574,2.25266 8.61505,4.95975 13.24469,7.09744 6.31848,2.91748 12.02929,8.25071 18.31714,10.36225 7.15423,2.40249 16.59692,2.23895 23.24865,5.96182 4.30609,2.41007 7.82721,3.57828 11.27204,6.3877 4.51593,3.68292 8.03363,3.98141 9.86309,9.51055 1.21582,3.67466 -1.17602,11.31769 -1.97265,15.33045 -0.84967,4.27991 0.98633,11.48321 0.98633,16.46606 0,6.71948 2.39532,11.99341 2.39532,18.87915 0,6.46686 -0.42273,12.39557 -0.42273,18.87915 0,8.52381 2.61469,16.11963 5.35425,23.84741 2.58887,7.30274 -1.55243,10.59089 -6.34052,13.48511 -6.91436,4.17944 -15.63916,2.90979 -23.67138,4.40039 -9.49646,1.76239 -17.50959,3.95185 -26.63025,5.96186 -8.05005,1.77404 -15.88288,1.48638 -23.67139,2.98089 -7.61114,1.46057 -14.39233,4.50195 -21.69873,6.95551 -6.50732,2.18524 -12.83508,0.53033 -19.1625,-0.42585 -6.56171,-0.99157 -10.47632,-4.19067 -16.90812,-4.9682 -6.76742,-0.81811 -12.04074,-5.96032 -17.61261,-9.9364 -7.67102,-5.47406 -17.70843,-2.40179 -26.20758,-4.54236 -7.63553,-1.92309 -15.42312,1.27704 -22.68503,-2.55505 -7.37866,-3.8938 -12.57974,-2.83899 -20.71243,-2.83899 -2.38367,0 -4.56894,1.33417 -6.48144,2.41309"
- id="path1484"
- inkscape:connector-curvature="0"
- style="fill:#d3ccb0;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 503,437.09448 c -11.96524,0 -1.74832,19.7526 1,24 2.83185,4.3765 -5,14.87534 -5,20.42856 0,10.26154 3,18.92029 3,29.57144 0,8.11493 3.26858,22.19837 -3,27.57141 -4.17276,3.57666 1.70114,19.36652 -1.42856,25 -6.27191,11.28949 1.38251,21.27069 2.42856,32.42859 1.19849,12.78406 -12.41104,8.01264 -18,7.57141 -8.04196,-0.63489 -18.39667,-1.44244 -26,-3.57141 -15.46887,-4.3313 -10.98203,-6.42859 -31.42856,-6.42859 -12.98767,0 -20.79376,-1.62701 -32.57144,-4.57141 -7.8869,-1.97174 -12.76981,2.23017 -19,-4 -6.57398,-6.57397 -5.27603,-18.62506 -3,-26.42859 2.422,-8.30395 0.96902,-19.25616 7,-25 3.24579,-3.09118 -0.0404,-20.1535 0.42856,-24.99997 0.78165,-8.07696 3.12167,-13.64322 7.57144,-20 7.82398,-11.17709 15.48541,-11.39132 27.42856,-15.57144 12.44394,-4.35538 15.13882,-14 30,-14 12.98733,0 21.69116,-8.82269 32.57144,-15.42856 1.43854,-0.87341 3.04761,-1.42859 4.57144,-2.14288 4.48056,-2.10025 13.25229,-3.70312 18.42856,-5.42856"
- id="path2246"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21672);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 616,482.09448 c -4.39838,-2.3656 -13.41223,-9.10306 -21,-11 -4.59033,-1.14758 -7.45984,-2 -12.57141,-2 -8.69379,0 -15.05536,-5.31338 -22.42859,-9 -9.62695,-4.81347 -18.14179,-11.41906 -27.57141,-16.42856 -6.20044,-3.29397 -13.02002,-5.92218 -19.42859,-7.14288 -0.32745,-0.0623 -0.66669,0 -1,0 -7.52756,0 -8.63477,-0.52426 -11,6.57144 -1.97052,5.91156 2,12.60154 2,18.42856 0,7.03616 1,13.5954 1,20.57144 0,10.9289 0,20.97327 0,32 0,9.17701 0.5863,15.43775 0.5863,25 0,9.81671 8.05329,13.37439 0.96955,24 -3.8295,5.74421 -10.77158,12.65369 -8.12729,19 4.11749,9.88196 3.57144,13.50519 3.57144,23 0,9.98108 9.79422,4.23633 16,3.57141 8.64905,-0.92663 14.60248,-5.16973 23,0.42859 5.35883,3.57251 22.07971,-7.61981 26,-10 5.24713,-3.18579 20.21448,-0.55365 26,-2 9.21045,-2.30261 13.18286,-8 22.57141,-8 6.81873,0 3.68378,-11.84234 2.42859,-15.42859 -3.40063,-9.71606 -9,-13.41192 -9,-24.57141 0,-11.24438 3.474,-19.81799 4.42859,-30 C 613.23236,510.52081 609,504.90683 609,496.52304 c 0,-9.64319 -2.43652,-10.67288 -8,-17.42856 -2.41754,-2.9356 -5.42511,-3.42508 -8,-6"
- id="path3014"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21674);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 502,453.66592 c 1.11188,1.58838 -12.29376,4.49552 -20,10 -6.2081,4.43436 -13.61762,6.17365 -20,10.42856 -1,0.66666 -2,1.33335 -3,2 -3.01025,2.00684 -10.92633,1 -14.42856,1 -8.15872,0 -17.31616,2.37238 -24.57144,6 -5.85278,2.92639 -11.32587,3.42856 -18,3.42856 -4.45447,0 -11.95877,2.52139 -14,5 -2.31937,2.81638 -1,10.15729 -1,13.57144 0,5.29779 -0.47635,7.94428 3,12 2.23343,2.60565 5.74423,-3.91473 9,-5 5.3421,-1.7807 10.11334,-3 16,-3 6.4115,0 12.01056,-0.25308 18,-2 6.65744,-1.94174 10.6048,-3 18,-3 5.33325,0 10.90695,1.61048 16.42856,0 3.39731,-0.99087 14.24634,-0.42856 18.57144,-0.42856 5.44379,0 8.28506,-4.3356 11,-6.57144 3.23935,-2.66769 0.33386,-17.01028 0,-20.57144 -1.00855,-10.75775 -2.32217,-15.88364 5,-22.85712 z"
- id="path3786"
- inkscape:connector-curvature="0"
- style="fill:#bae0ff;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 495,529.09448 c -0.45303,-4.83221 -9.32635,2.69141 -14,4 -5.42462,1.51886 -12.33929,2 -18,2 -6.13174,0 -11.11127,-1 -17,-1 -7.33334,0 -14.66666,0 -22,0 -7.83707,0 -16.2467,1 -24.42856,1 -4.04764,0 -8.09524,0 -12.14288,0 -4.95023,0 -2.71091,10.55969 -2.42856,13.57141 0.42044,4.48474 2.04804,7.88654 4.57144,9.42859 2.77924,1.69843 14.33438,0.57141 17.42856,0.57141 5.70303,0 11.27673,1.42859 17.57144,1.42859 5.37241,0 8.52683,3.57141 13.42856,3.57141 5.13855,0 11.70508,-0.003 16,1.42859 7.83749,2.61249 12.48306,9.49433 20,12 4.70148,1.56714 10.15369,2 11,2 0.43362,0 4.61545,-15.46185 5,-17 2.37042,-9.48169 0.47669,-15.09326 -2,-25 -1.39148,-5.56586 -0.71887,-5.86236 1.57144,-8"
- id="path3788"
- inkscape:connector-curvature="0"
- style="fill:#bae0ff;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 589.57141,506.09448 c -0.78101,-3.12405 -4.44739,5.219 -7.57141,6 -1.51324,0.3783 -3.04761,0.66669 -4.57141,1 -4.30866,0.94251 -16.49841,-5.61954 -21.42859,-7 -8.89459,-2.49048 -10.9043,-2.54681 -20,0 -7.19501,2.01459 -18.20782,0.78931 -24,-2.42856 -4.85602,-2.69781 -2.42856,-18.00323 -2.42856,-22.57144 0,-5.65188 -2.14288,-10.99628 -2.14288,-17 0,-5.50784 0.81683,-6.90842 5.00003,-9 3.50134,-1.75067 11.01233,6.29392 13.57141,8 4.28803,2.85867 9.90851,5.57437 15,7 6.66467,1.86612 13.68353,4.14105 19,8.57144 4.16937,3.47446 6.39374,2.63065 10,1.42856 3.83124,-1.2771 8.26587,2.17725 11,4 3.00665,2.00443 3.45422,10.75388 5,14 2.06726,4.34122 -1.53869,12.12851 -4,15"
- id="path3790"
- inkscape:connector-curvature="0"
- style="fill:#bae0ff;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 387,511.52304 c -0.82404,-0.34354 0.5191,-16.73843 5.42856,-6.42856 2.65344,5.57218 0.0641,-14.08227 10.57144,1.42856 0.98492,1.45392 2.33402,-3.98389 4,-3.42856 9.67755,3.22586 -8.01267,7.26108 6,-5 3.69855,-3.23623 2.11939,14.16437 4.42856,-2 2.80047,-19.60315 1.35476,13.37171 8.57144,-9 0.0201,-0.0621 -2.24557,17.10523 3.57144,6 1.17703,-2.24707 -2.17312,23.04657 2.85712,3.42856 7.38669,-28.80804 0.55865,14.66568 9.57144,-1.42856 11.34888,-20.26584 4.30826,19.2572 10.42856,-4 5.1058,-19.40198 7.80136,13.81385 12.14288,0 9.05142,-28.79999 6.82193,24.42652 14.42856,-6 3.06287,-12.25152 10.85675,1.12357 9,6.42856 -0.45621,1.30344 1.7767,-3.49789 3,-2.85712 6.68454,3.50141 -0.23389,13.17319 -4,14.42856 -4.52319,1.50772 -15.65384,2.16483 -19,0.57144 -4.36935,-2.08065 -13.41278,0.42856 -18.42856,0.42856 -8.82132,0 -16.93136,0 -25.57144,0 -5.93942,0 -15.30438,3.48654 -21,4.57144 -4.53558,0.8639 -16,11.84397 -16,-0.57144 0,-4.9397 -0.14462,-0.57849 -1,-4"
- id="path6858"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21676);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 385,557.09448 c 0,-4.75567 -0.9375,-21.56879 6,-7 1.02548,2.15357 -0.0686,-7.69482 6.42856,4 0.52432,0.94373 1.42859,1.61902 2.14288,2.42859 2.09979,2.37976 2.66809,-5.7879 4.42856,-8.42859 8.35098,-12.52649 -4.39771,18.59656 12,-6 2.14865,-3.22302 2.52551,21.40137 4,11.57141 2.23669,-14.91137 12.45001,-14.14288 15,2.85718 0.66525,4.43494 5.04697,-7.88232 9,-10 1.63327,-0.875 3.33334,-1.61908 5,-2.42859 0.94507,-0.45904 -4.20697,15.27594 7,9 8.09897,-4.5354 -1.15662,22.98279 9,5.57141 0.59009,-1.01153 2.44107,13.34107 4.57144,-1.57141 0.23172,-1.62207 1.90476,-2.66669 2.85712,-4 0.3045,-0.42627 0.28149,8.72492 4.57144,-2 3.06708,-7.66766 -0.16611,6.90412 8,-2.42859 0.67349,-0.76965 5,20.20618 5,23.42859 0,6.01734 -2.358,7.06018 -7.57144,6.57141 -6.47071,-0.60663 -8.1995,-6.37695 -15.42856,-5 -0.95084,0.18115 -12.39166,-6.76727 -14,-7.57141 -4.78571,-2.39282 -9.17377,0.24097 -13.42856,-1 -4.3786,-1.2771 -8.82895,-3 -13.57144,-3 -3.9924,0 -5.59308,-1 -9,-1 -4.47974,0 -9.03964,0.24011 -14,-1 -0.98077,-0.24518 -8.4931,1.50098 -12,1 -7.41922,-1.05987 -7.04337,-9.43494 -4,-14 1.83841,-2.75763 0.10953,-0.32855 1,-3"
- id="path6860"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21678);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 511,503.09448 c 3.16498,-4.74746 -3.80432,-11.58706 -2,-17 6.19183,-18.57553 3.21765,11.34711 9,-6 4.05298,-12.1589 4.40607,16.50046 11,1.42856 0.38373,-0.87711 3,22.82816 3,11.57144 0,-17.43216 8.94373,6.83118 9,7 1.22296,3.66895 0.0513,-8.08798 2,-11.42856 11.26154,-19.30548 0.67804,29.71646 8,0.42856 3.85071,-15.40283 14.21442,16.61255 10,4.57144 -1.11426,-3.18368 3.52411,7.08405 8,9.42856 8.16742,4.27817 6.19531,-17.51175 12,-3 0.058,0.1449 2.09918,-9.09176 5,-3 1.40387,2.94818 4.07721,3.76834 2,10 -2.35309,7.05933 -2.55048,4.42856 -10.57141,4.42856 -1.66669,0 -3.33338,0 -5,0 -6.44739,0 -16.46368,-5.44025 -22.42859,-7.42856 -6.35217,-2.11737 -13.64484,0.11838 -20,-2 -4.4104,-1.47015 -10.93445,1.43823 -15.42859,2 -1.22668,0.15335 -2.38092,-0.66665 -3.57141,-1 z"
- id="path6862"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21680);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 390,489.09448 c -2.83557,9.04932 1.14993,4.92502 5,3 2.69357,-1.34677 4.01419,7.98581 11,1 3.14642,-3.14642 8.74765,3.87253 10.42651,-4 0.95688,-4.48694 8.57349,-4.35144 8.57349,1 0,4.53018 15.76996,-3 16,-3 5.03445,0 -1.52841,7.52841 8,-2 5.3418,-5.34179 7.74686,-8.60949 16.41891,-1 7.37473,6.47113 3.43299,-0.81845 14.14212,-5 4.88181,-1.90616 7.44617,3.80008 10.43897,-3.40869 2.41434,-5.81543 11.20276,-3.58465 8,-11.59131 -2.69083,-6.7269 -8.77499,-4.01559 -11,0.47962 -1.51907,3.069 -13.11627,5.07852 -16,6.52038 -6.34668,3.17334 -5.10895,6 -13,6 -6.93597,0 -12.75632,3 -20,3 -4.67987,0 -12.18323,0.59162 -17,3 -4.50354,2.25177 -13.90854,2 -19,2 -2.31934,0 -11.85815,3.71634 -13,6 -0.65839,1.31681 1.67145,4.3429 2,5"
- id="path9908"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-opacity:0.47511297;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 588,489.09448 c -2.72504,5.45011 -5.23505,0.88248 -11,-2 -2.45093,-1.22546 -3.97754,3.99109 -6.52521,5 -3.98163,1.57675 -10.3858,-8.9555 -10.47479,-9 -0.8277,-0.41385 -11.67548,1.66226 -17,-1 -1.54163,-0.77081 -1.71326,5.28678 -7,0 -2.58325,-2.58328 -6.49817,11.75272 -13,2 -5.98621,-8.97931 6.43426,-1 -5,-1 -3.91602,0 -3.51306,-9.18307 -6,-10.43915 -3.11118,-1.57141 -3,-8.31039 -3,-11.56085 0,-5.62314 6.42291,0.43109 7.43158,1.45932 1.75159,1.78543 7.21668,5.3103 9.09143,6.06092 0.96228,0.38528 8.08148,0.3749 10.47699,1.01013 1.28674,0.34125 2.76874,1.51904 7,2.46963 2.12708,0.47785 8.37293,2.68646 11,4 4.97095,2.48548 10.11395,2.05698 14,4 3.2157,1.60785 8.69208,1.06345 11,2 3.84772,1.5614 7.12799,0.25598 9,4 1.00891,2.01788 0.75171,4.50345 2,7"
- id="path9910"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-opacity:0.47511297;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 385,543.09448 c 1.27365,2.5473 0.45267,-6.72632 3,-8 4.89444,-2.4472 9.28613,0.9046 15,-1 4.68838,-1.5628 8.1954,2.40228 13,0 3.09979,-1.54986 5.77383,0 10,0 6.70081,0 15.34793,-0.82605 21,2 4.10953,2.05475 17.78503,-1.18622 21.44937,-3 3.64453,-1.80395 9.89139,-1 14.14212,-1 6.05768,0 7.40851,4.62396 7.40851,11 0,9.85272 -3.19483,1.56671 -9.4288,-1 -9.03504,-3.71997 -4.14771,9.82923 -17.5712,2 -7.44434,-4.34192 -4.32449,3.83777 -12,0 -12.37015,-6.18506 0.7341,-7.73413 -9,2 -5.22446,5.22443 -11.45896,-2.54907 -12.4415,-7 -0.47537,-2.15344 -5.79849,3 -8.08124,3 -5.86953,0 -5.56933,-6.72381 -8.47726,2 -1.26563,3.79682 -9.11676,-4 -13,-4 -2.14655,0 2.0787,6.69293 -9,3 -1.26953,-0.42315 -3.47315,1.26343 -6,0 z"
- id="path9912"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-opacity:0.47511297;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 531.58392,362.54868 c -0.77856,0 -0.77856,0 0,0 4.65033,0 -12.05798,2.39224 -20.58392,4.5458 -5.26334,1.32947 -10.81781,5.09961 -16,6.56589 -3.27487,0.9266 -5.49826,4.68326 -9,6.43411 -4.60718,2.30359 -6.95679,5.51261 -10.47955,9 -3.28485,3.25183 -5.0026,7.40961 -9.0914,9 -5.00257,1.94583 -8.9177,7.48865 -12.42905,11 -3.23248,3.23249 -4.15076,4.77149 -6,8.50754 -1.56494,3.16165 1.4704,5.96286 3,7.49246 3.25296,3.25296 11.45843,6 16,6 6.3468,0 12.43182,-0.21591 18,-3 4.93979,-2.46991 11.10431,-4.36047 16,-5.44171 5.89151,-1.30118 13.31122,-4.33972 17,-9.09137 0.42334,-0.54535 0.97473,-0.97796 1.4621,-1.46692 3.1997,-3.21032 5.90606,-4.69345 8.08117,-9 3.00556,-5.95071 8.45673,-6.41702 8.45673,-13 0,-4.59155 1,-10.02252 1,-15 0,-3.73642 0.73279,-10.11438 -3,-11 -3.50458,-0.83148 -7.46368,-1.5458 -11.50745,-1.5458"
- id="path10674"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21682);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 541,383.09448 c -3.26422,-1.79321 -1,-8.05395 -1,-13.47473 0,-4.08874 -1.57178,-9.38174 -4,-4.52527 -0.33331,0.66666 -0.66669,1.33335 -1,2 -1.34747,2.69495 -3.06103,5.29697 -4.42627,8 -1.00244,1.9848 -3.1048,4.03681 -5.05072,5 -3.23059,1.59906 -3.97864,4.46933 -7.07111,6 -3.5213,1.74298 -5.8894,4.71875 -8.4519,6 -4.53449,2.26724 -6.8371,5.41855 -12,8 -5.05209,2.52606 -8.47403,4.22425 -13,7.40601 -3.39267,2.38504 -9.29419,3.55951 -13,5.05075 -3.05395,1.22894 -5.85962,4.47479 -9,6.06091 -4.00662,2.02365 -8.35986,3.67838 -12,4.48233 -0.52701,0.1164 3.9975,5 7,5 5.33334,0 10.66666,0 16,0 3.90048,0 4.88745,3.83341 8.59149,2 4.84302,-2.39715 11.53064,-3.56408 16.40851,-5 5.35266,-1.57568 10.01999,-4.13962 15.41132,-6.48233 3.59124,-1.56051 7.54809,-5.52783 10.1015,-8.0812 3.89196,-3.89197 8.09485,-7.65174 10.48718,-12.43647 3.20587,-6.41168 6,-10.72476 6,-18 0,-3.66665 0,-7.33334 0,-11"
- id="path11442"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21684);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 381.04974,484.37271 c 20.40573,-10.32132 40.84214,-20.67355 61.70816,-30.09341 22.98368,-7.12235 44.72333,-27.32148 69.41111,-27.2551 16.66876,7.44907 29.90362,20.4437 46.52856,28.17441 15.71008,6.98315 32.12262,12.27145 48.39606,17.77994 13.4884,2.88238 15.39032,9.01098 15.1286,21.86221 -1.133,20.36487 2.16339,40.53467 3.30017,60.80707 -0.59357,11.54016 4.39233,24.29297 0.91455,34.77887 -23.73639,8.23059 -49.78412,12.31933 -74.87988,17.3053 -12.14356,2.69689 -24.48621,4.17309 -36.69519,6.48938 -10.57862,1.78033 -21.01404,3.28179 -31.42368,0.19647 -15.39944,-6.18604 -30.86749,-11.83307 -47.03217,-15.67743 -19.69281,-4.5849 -40.00018,-4.38556 -60.08746,-4.77838 -19.13516,-5.00647 -12.84207,-31.86475 -10.75766,-46.36353 1.96967,-15.66638 5.08887,-31.2691 4.51962,-47.10376 l 9.57413,-4.93591 c -0.0832,16.17801 -3.4378,31.98437 -5.78595,47.94861 -1.81418,13.84625 -6.84491,39.56811 11.27536,44.12292 19.84284,1.09235 39.90369,0.98682 59.37967,5.49915 16.39639,3.96185 32.27557,9.11712 48.06903,15.031 10.38046,2.88642 20.74725,1.10907 31.22193,-0.68261 11.88659,-2.19239 23.8966,-3.48682 35.76489,-5.81708 10.16357,-1.99524 71.23883,-17.4776 56.16144,-7.08289 5.26703,-9.07129 -0.36548,-23.23114 0.77642,-34.2857 -1.10992,-20.21253 -3.61694,-40.34217 -3.78332,-60.60767 -0.59161,-12.11716 -1.82105,-17.94641 -14.92023,-20.4266 -15.90155,-5.90213 -31.57312,-12.46073 -47.39404,-18.57053 -16.80481,-7.5213 -29.93707,-20.67361 -46.87781,-27.54187 -24.00693,0.62009 -44.97717,19.3848 -67.38959,26.37653 -21.96521,10.0061 -43.39273,21.40167 -64.95462,32.00842 l 9.8519,-7.15781 z"
- id="path1909"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 501.33227,615.45538 c -5.95517,-5.93792 -6.83361,-15.52533 -6.7955,-23.56506 -0.10562,-12.59235 5.09503,-23.96008 8.76062,-35.6897 2.3418,-7.04065 -1.36914,-12.66528 -4.01245,-18.90356 -2.17377,-6.58734 -0.0911,-13.20862 2.02716,-19.50934 1.91349,-6.43619 -0.35235,-13.04642 -1.1474,-19.51169 -0.83359,-19.60269 0.0399,-39.4693 2.41367,-58.96664 l 3.68408,-1.80985 c -3.30768,19.25071 -4.30795,39.05726 -1.69525,58.45279 1.08285,6.44733 3.74976,12.99262 1.71512,19.48706 -2.02735,6.26563 -4.54557,12.6438 -2.29636,19.23877 2.64893,6.36097 6.5802,12.07782 4.10724,19.26148 -4.12753,11.72345 -9.69897,23.10022 -9.4942,35.87811 0.12097,7.63239 1.00946,17.48505 7.65921,22.05872 l -4.92594,3.57892 z"
- id="path1919"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 502.6969,452.49924 c -8.78708,9.32873 -20.96893,15.44421 -32.69824,20.43112 -22.9665,6.10425 -46.74933,8.76059 -70.07685,13.29904 -11.03021,3.17111 -8.50937,-0.62326 -11.38614,10.89417 -1.15875,4.8164 -0.63601,9.72061 -0.33032,14.59353 1.84076,4.62567 6.5445,1.25916 10.06592,-0.15603 8.72965,-2.11667 17.56403,-4.22556 26.51669,-5.2436 11.87674,-1.0986 23.79749,-1.48608 35.7193,-1.64532 11.03003,-0.0622 22.02838,0.26587 33.05265,-0.33929 l -3.38599,2.47772 c -11.02533,0.13171 -22.0484,0.15533 -33.07202,0.34213 -11.94086,0.13443 -23.87219,0.72247 -35.76895,1.73706 -9.06568,1.09271 -18.03128,3.34967 -26.84494,5.6405 -3.81369,1.49268 -9.15833,5.1164 -10.75662,-0.47558 -0.27225,-4.95636 -0.78473,-9.94934 0.44461,-14.83106 2.42688,-9.72519 8.71054,-14.31976 18.957,-15.41485 23.42068,-3.75495 47.32806,-6.19144 69.99783,-13.35263 9.76761,-4.16642 16.8945,-7.71512 24.74371,-15.49979 l 4.82236,-2.45712 z"
- id="path1927"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 491.60593,532.39941 c -6.47925,2.40778 -13.67038,2.72522 -20.56635,3.04035 -10.06824,0.24505 -20.14114,0.22156 -30.21182,0.297 -8.38934,0.0387 -16.77896,-0.003 -25.1683,-0.0184 -7.5933,-0.0161 -15.2099,-0.21582 -22.78217,-0.82129 -7.70392,0.47046 -4.05789,-3.65716 -5.15732,4.42853 -0.37619,5.10315 0.19446,10.2107 0.42255,15.31171 0.85797,4.44397 5.24875,3.82465 9.07184,3.93707 7.12445,0.2038 14.28237,0.31458 21.38879,0.87927 9.28787,0.46332 18.54517,1.25031 27.55865,3.62067 8.05381,1.48786 15.98676,3.18646 23.62436,6.17975 5.08106,2.07336 9.95169,4.57208 14.80506,7.12024 2.68649,1.51544 5.48449,2.81567 8.20047,4.27557 l -4.27115,2.7937 c -2.71008,-1.42425 -5.43567,-2.82135 -8.09003,-4.34802 -4.79529,-2.55835 -9.6297,-5.01678 -14.62219,-7.17364 -7.50855,-3.00031 -15.38348,-4.70215 -23.28818,-6.33374 -9.16162,-2.10388 -18.40539,-3.21234 -27.81704,-3.35095 -7.13688,-0.58234 -14.29108,-0.77387 -21.44736,-0.80249 -4.05991,-0.12629 -9.03269,0.58032 -9.56528,-4.51392 -0.10763,-5.1286 -0.71887,-10.23627 -0.44015,-15.36438 0.76977,-7.10547 6.47363,-10.66821 13.57754,-9.4776 7.47531,0.68701 15.01349,0.88818 22.51547,0.96307 8.40927,-0.0157 16.8187,-0.0553 25.22797,-0.0254 10.03149,0.0587 20.0672,0.10968 30.09448,-0.2345 7.22202,-0.37194 14.56,-0.83758 21.31879,-3.56384 l -4.37863,3.18128 z"
- id="path1929"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 508.25122,454.93243 c 4.04596,0.44638 7.69959,2.14151 11.12708,4.28739 5.99054,4.27807 12.58923,7.30188 19.24841,10.33984 12.22534,5.90704 25.63507,7.98535 38.83447,10.42917 7.65772,1.12894 13.37708,2.4852 14.09424,10.8013 0.23096,5.84375 0.0347,11.6889 0.53546,17.51666 -2.7077,8.53428 -13.68347,3.81668 -19.88391,2.50678 -12.0318,-2.68622 -24.37817,-3.52744 -36.61926,-4.78265 -7.55677,-0.58179 -15.16712,-1.21146 -22.74067,-0.64063 -0.70813,0.01 -1.38949,0.2236 -2.0802,0.33475 l 4.20728,-3.15763 c 0.61023,-0.0322 1.22028,-0.0673 1.83063,-0.0967 7.44085,-0.39718 14.89306,0.21253 22.28442,1.07117 12.28461,1.29279 24.62079,2.31714 36.75263,4.73731 5.4237,0.99295 17.4414,0.26458 10.6499,4.37552 -0.66083,0.40008 1.86633,-1.93717 1.10058,-2.03949 -0.34234,-5.86631 -0.003,-11.74762 -0.28393,-17.62174 -0.86347,-7.73541 -6.19776,-9.19351 -13.36231,-10.55691 -13.18426,-2.9285 -26.69397,-4.6471 -39.10052,-10.35972 -6.82953,-2.86035 -13.40076,-5.97683 -19.4964,-10.24935 -3.54596,-2.03989 -7.30243,-3.82258 -11.47653,-3.71384 l 4.37863,-3.18125 z"
- id="path1931"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 474.53879,428.21323 c 15.08642,1.24041 31.55124,-5.12519 42.64322,-12.79139 11.24139,-7.83841 17.85949,-18.91751 19.47363,-32.32736 0.6947,-4.85229 0.0286,-9.60943 -0.73908,-14.38662 l 7.18738,-3.83259 c 0.92383,4.90302 1.24945,9.81043 0.48328,14.77909 -1.70459,13.9787 -9.11994,25.42453 -20.75049,33.54336 -16.22406,11.26267 -35.24899,19.61515 -55.26428,20.07685 l 6.96634,-5.06134 z"
- id="path1965"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="M 531.03699,365.59436 C 520.763,365.25906 510.61935,368.39987 501,371.7211 c -10.29706,4.98694 -15.06458,7.68494 -30.36362,19.32165 -8.6427,7.68662 -21.97839,16.05356 -22.44922,28.70917 0.90741,2.24188 2.4115,2.25568 4.42514,2.89005 l -6.74041,4.75525 c -2.26953,-0.95469 -3.97641,-1.21174 -4.83023,-3.86969 0.8869,-13.19736 14.07946,-21.76319 23.21524,-29.93918 13.11264,-10.17749 27.37905,-19.34586 42.42999,-26.30054 10.15576,-3.28284 20.63843,-5.99194 31.31647,-6.75482 l -6.96637,5.06137 z"
- id="path1967"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 540.46417,357.15597 c -1.39947,10.84278 -9.79535,19.64624 -17.24609,27.08002 -15.0246,13.78653 -33.59287,23.07712 -52.30042,30.84082 -6.83416,3.04779 -14.07617,4.65656 -21.42502,5.80551 l 6.00788,-4.49337 c 7.18216,-1.07413 14.22498,-2.68485 20.90625,-5.63514 18.45926,-7.41894 24.7359,-10.41153 40.38626,-23.85992 7.09442,-6.98062 15.63702,-15.39536 15.99878,-25.82867 l 7.67236,-3.90925 z"
- id="path1969"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 536.15497,397.20346 c -5.35584,10.0798 -16.58478,15.52066 -26.27924,20.78888 -1.53991,1.57248 -8.30908,10.62735 2.32416,11.95703 3.59869,0.901 13.11853,-4.47247 7.51337,-10.5184 -5.74701,0.053 -1.56189,-4.21912 -1.30487,2.99707 l -4.84809,2.69369 c -0.47796,-6.05944 4.2309,-10.56417 10.69788,-8.6397 8.07202,7.19479 -9.61798,16.19845 -16.1929,16.43267 -14.89362,-1.87149 -2.08869,-14.98413 5.70108,-18.05655 2.78613,-1.50686 12.54425,-6.22354 17.27374,-15.04852 l 5.11487,-2.60617 z"
- id="path1985"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 456.21182,396.72199 c -8.40521,6.78039 8.94049,-7.96436 11.10984,-16.96311 0.68084,-6.7655 -0.92032,-21.96774 9.88143,-14.3992 13.00477,6.27307 28.1276,4.60538 42.04471,2.94318 13.83881,-3.61508 2.84265,0.27386 12.79407,-3.36579 l -5.1709,2.18824 c 14.15387,-6.41404 -6.81958,5.27145 -11.50793,4.07067 -14.04727,1.38785 -29.40485,2.98468 -42.56256,-2.98574 -5.60401,-3.38599 3.41827,-10.13956 -0.99375,9.37289 -2.55722,11.39615 -10.01385,17.49707 -20.23916,22.51309 l 4.64425,-3.37423 z"
- id="path1999"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 474.47821,408.71603 c 3.04907,-10.04556 1.99292,-20.60171 0.73654,-30.86749 -0.47723,-3.25696 -1.56894,-6.32696 -2.72217,-9.38303 l 4.60031,-2.56677 c 1.03669,3.16934 1.99604,6.35489 2.4256,9.68112 1.13303,10.14039 2.17768,20.47248 0.0746,30.53 l -5.1149,2.60617 z"
- id="path2001"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 449.2579,421.87994 c 5.75421,7.54642 15.14914,7.64142 23.90818,7.35682 l -3.37345,2.46399 c -9.62906,0.17172 -17.93808,0.2099 -25.45108,-6.24888 l 4.91635,-3.57193 z"
- id="path1427"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer12"
- display="none"
- style="display:none">
- <g
- id="g21713"
- transform="matrix(0.903793,0,0,0.903793,-1375.927,-674.5076)">
- <path
- d="m 785,560.09448 c -0.81195,1.33539 -6.30042,5.15021 -10,7 -4.65833,2.32916 -8.62067,1.08045 -13,4 -3.83227,2.55487 -10.31458,1.29114 -14.57141,4 -5.00342,3.18396 -9.89612,3.19104 -14.42859,6.42859 -4.77478,3.41052 -8.51886,3.43994 -10.57141,8.57141 -1.29938,3.24841 -1.42859,7.15119 -1.42859,11.42859 0,2.34064 6.72479,10.8479 8.57141,12.57141 3.54651,3.31006 9.72839,4 15.42859,4 5.28656,0 12.64545,0.6518 16,3 4.17584,2.9231 12.20697,4 17.57141,4 1.15173,0 2.28571,0.28571 3.42859,0.42859 1.08179,0.13519 9.00085,3.57202 10.42859,4.57141 0.40021,0.28015 0.71429,0.66669 1.07141,1 3.73248,3.48364 9.5,5 15.5,8.5 5.74085,5.74085 12.14172,-1.64227 15,-3.07141 4.21844,-2.10925 7.29156,-3.98151 10,-7.42859 1.95215,-2.48456 7.10553,-4.10553 9.5,-6.5 3.83917,-3.83917 10.50452,-6.50226 14.5,-8.5 3.98743,-1.99371 7,-9.78448 7,-14 0,-5.26251 2.4538,-10.06604 -1,-15 -4.14459,-5.9209 -0.75781,-4.57397 -6.57141,-10 -3.55908,-3.32178 -12.00794,-2 -16.42859,-2 -7.5426,0 -13.73877,0.13062 -20,-3 -6.23224,-3.11615 -11.06152,-3 -18,-3 -5.92011,0 -9.37109,1.41931 -14.5,-2 -3.3208,-2.21387 -5.53687,-2.98248 -8.92859,-4"
- id="path13003"
- inkscape:connector-curvature="0"
- style="fill:#d4e4e4;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 867,582.09448 c -3.98486,2.04846 -6.87482,1.91657 -10,4 -3.44287,2.29529 -7.29456,3.14728 -11,5 -4.05774,2.02887 -8.51947,4.25971 -12,6 -2.8125,1.40625 -7.1496,4.22437 -9,7 -1.85138,2.7771 -1.22156,7.88617 -2,11 -0.91608,3.66431 0,6.3465 0,10 0,4.03534 0.47485,6.94971 2,10 0.6543,1.30866 7.92718,-3.85431 9,-6 0.67096,-1.34186 11.67493,-9 13,-9 2.4538,0 12.74182,-7.11273 14,-9 2.78259,-4.17395 4,-6.4837 4,-12 0,-4.79571 -0.42859,-9.70337 -0.42859,-15 0,-0.74353 -0.72253,-1.0116 -1.07141,-1.5"
- id="path13763"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21735);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 817.42859,607.59448 c 0,-0.92102 -7.79163,-4.45831 -9.42859,-5.5 -5.1886,-3.30182 -9.55005,-3 -16,-3 -3.96759,0 -10.03729,-0.50934 -14,-1.5 -6.55273,-1.63818 -16.44244,0.39026 -22,-3.5 -3.40015,-2.38006 -7.88623,-2.52038 -11.42859,-5 -3.19104,-2.2337 -9.57141,0.11371 -9.57141,-5 0,-4.46014 7.56073,-4.69159 11,-5.42859 5.04437,-1.08093 10.55896,-5.29425 14,-8.57141 3.49341,-3.32709 11.88965,-1.94482 16,-4 4.47864,-2.23932 8.06519,-4.46741 13,-2 6.0838,3.04187 15.21448,3 22,3 1.33331,0 2.66669,0 4,0 4.71368,0 9.0044,3 14.57141,3 6.6695,0 10.42713,6.42859 19.42859,6.42859 1.01801,0 2,0.38092 3,0.57141 3.85474,0.73425 6.12195,3.24036 7,6 0.26886,0.84503 -12.8371,1.91852 -15,3 -3.34412,1.67206 -11.57141,9.86109 -11.57141,14 0,4.6258 -9.69977,5.62952 -13.42859,6.42859 -0.61987,0.13281 -1.04761,0.71429 -1.57141,1.07141 z"
- id="path13765"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21737);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 812,638.09448 c -1.84375,0.39508 3.15674,-2.31348 4,-4 1.68939,-3.37878 -1.4635,-3.90234 2,-6.5 4.53424,-3.40069 -0.51294,-14.01294 -3,-16.5 -3.44904,-3.44903 -5.06482,-6.24536 -9,-9 -3.85577,-2.69903 -13.41766,-3 -18,-3 -7.26636,0 -14.81293,0.12476 -21,-4 -4.47284,-2.98187 -13.10223,0.59851 -18.5,-3 -3.45215,-2.30145 -13.16669,-4 -17.5,-4 -5.56567,0 -7.11847,2.78034 -6,8 0.90234,4.211 -3.07251,7.05774 -1.5,12 1.87921,5.90595 6.37506,7.83331 12.5,9 7.34735,1.39948 16.75647,0.17096 22.5,4 5.08142,3.38757 10.58105,2.28107 16.07141,1 5.21857,-1.21765 10.12567,2.38111 12.42859,6 2.995,4.70642 10.8075,0.55469 14,5.57141 1.32227,2.07788 8.78174,3.31946 11,4.42859 z"
- id="path13767"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21739);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 783.5,623.09448 c 2.09235,-1.08093 2.11468,-6.22943 3.5,-9 1.45935,-2.91876 -1,-5.94488 -1,-9 0,-3.22912 -5.05933,-2 -8,-2 -3.66858,0 -7.16065,0 -10.57141,0 -3.45612,0 -5.96991,-1 -9.42859,-1 -4.1178,0 -6.00983,-2 -9,-2 -5.10168,0 -2.16504,3.2298 -1.57141,6 0.65106,3.03821 -0.17706,5.83472 1.07141,7 2.52844,2.35993 7.87274,0.32807 10.5,2 3.17969,2.02344 8.0022,2.5 12,2.5 3.97839,0 7.48413,3.65222 10,5 1.35077,0.72363 0.75677,0.5 2.5,0.5 z"
- id="path16067"
- inkscape:connector-curvature="0"
- style="fill:#57839e;fill-opacity:0.77375602;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 751.5575,605.63007 c -3.80701,2.87561 0.51062,-4.16907 3.86248,-3.65741 3.39386,1.47558 2.5752,2.59356 1.40375,5.04254 2.24048,2.09833 0.59168,-4.18952 5.26831,-1.24182 0.88282,3.66412 -4.55194,6.73224 0.13495,0.70025 1.89954,-3.43548 5.92389,-1.67999 3.14325,0.95893 1.45966,1.50317 6.70172,0.21112 0.77655,2.21093 -2.04346,-4.05383 3.97974,-7.59295 4.72876,-2.01434 1.94586,2.9762 -0.70398,3.94855 1.01849,1.15131 5.06623,-3.97113 2.91297,-0.86914 5.04657,1.75323 -2.2843,1.98816 -0.55017,-1.9126 3.88691,-3.43091 1.79974,4.3639 2.67456,4.89014 -1.78144,7.16205 0.85071,-0.55505 1.70148,-1.1101 2.55219,-1.66522 0.0205,0.006 0.041,0.012 0.0615,0.0179 l -2.51062,1.64618 c -0.0474,-0.0294 -0.0947,-0.0587 -0.14209,-0.088 0.82623,-0.5813 1.65241,-1.1626 2.47864,-1.74384 -2.74933,1.98255 -0.7619,-0.17504 -3.08405,-3.79358 3.78363,-3.30395 -0.90496,3.46155 -3.90075,3.48096 -1.69232,-2.51617 -1.27252,-4.8891 -0.0783,-4.44604 -1.52112,2.4049 -5.68878,5.94525 -5.94733,1.20355 -0.12866,-1.0423 -2.25995,-2.11859 -1.45575,-2.79401 3.34027,-2.8056 0.1366,-0.12439 1.62189,2.10584 -1.14441,0.85034 -4.61072,3.47625 -5.94696,0.40106 1.53076,-1.46783 1.13544,-4.35486 2.07611,-3.19751 -1.80395,2.1507 -6.44745,7.66339 -5.13751,1.83478 3.05139,-3.74384 -1.70264,2.2063 -5.34503,1.33832 0.31823,-2.32251 2.87225,-3.69654 -1.13904,-5.00647 1.9541,-2.4776 -0.0673,2.41589 -4.04962,3.8573 l 2.45813,-1.78595 z"
- id="path16835"
- inkscape:connector-curvature="0"
- style="fill:#c9f4ff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 751.5575,609.91577 c 2.1997,0.023 4.38598,0.22418 6.57135,0.47266 l -2.21851,1.56164 c -2.25824,-0.33416 -4.53162,-0.37799 -6.81097,-0.24835 l 2.45813,-1.78595 z"
- id="path16837"
- inkscape:connector-curvature="0"
- style="fill:#c9f4ff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 760.00348,614.31293 c 1.94025,-2.06397 4.50098,-4.24994 5.60663,-0.48133 -3.0636,3.17499 0.13293,-2.27771 3.70556,-0.10522 1.57184,0.63006 2.97217,-0.38745 4.68726,-0.0825 1.55908,0.57483 3.22516,0.66327 4.86322,0.88397 0.47778,0.005 0.93323,0.16559 1.39953,0.24835 l -2.40252,1.65467 c -0.44312,-0.0805 -0.8783,-0.25165 -1.33881,-0.24317 -1.65106,-0.24945 -3.33026,-0.30182 -4.90283,-0.90088 -1.69397,-0.19696 -3.21545,0.91748 -4.75659,-0.0106 -3.31049,-1.82995 4.66662,-2.41919 -3.71375,-0.11493 -0.86993,-3.27276 -3.29913,-0.87072 -0.44037,-2.22778 l -2.70733,1.37946 z"
- id="path16839"
- inkscape:connector-curvature="0"
- style="fill:#c9f4ff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 795,624.09448 c 1.19098,0.29242 3.97101,1 7,1 2.79755,0 5,-1.00073 5,-4 0,-2.63623 1.70471,-0.0223 0,-4 -1.00903,-2.35431 -1.70551,-5 -5,-5 -0.0926,0 -2.82477,-1 -4,-1 -2.09992,0 -5,3.46448 -5,6 0,2.59522 3.13611,5.50812 4,6.57141"
- id="path17667"
- inkscape:connector-curvature="0"
- style="fill:#accce5;fill-opacity:0.904977;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 732,601.52307 c 0.19122,0.23535 -6,1.73926 -6,-3.42859 0,-2.93768 7,-4.90362 7,-0.5 0,0.3858 -0.71252,2.9704 -1,3.92859 z"
- id="path18427"
- inkscape:connector-curvature="0"
- style="fill:#8db6ce;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 727.42859,601.09448 c 0,1.23627 3.57141,0.23627 3.57141,-1 0,-3.76623 0.23859,-4 -3.57141,-4 -1.62512,0 -1.00818,5 1.57141,5"
- id="path19187"
- inkscape:connector-curvature="0"
- style="fill:#cdeef6;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 802,618.09448 c 0.0153,-0.66058 -1.61615,-3 -2.57208,-3 -2.29212,0 -0.60938,2 -2.42792,2 -1.99359,0 2,3.8501 2,5 0,0.14984 4,0.61804 4,-3 0,-2.81201 -2.15015,-3.15014 -4,-5"
- id="path20705"
- inkscape:connector-curvature="0"
- style="fill:#7b97b4;fill-opacity:0.77375602;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 784.42157,558.80048 c -3.87299,5.67157 -11.16852,9.52588 -17.92267,11.11737 -4.48321,0.81292 -8.79577,2.27405 -13.18341,3.45819 -5.41113,1.99786 -11.00024,3.52789 -16.33459,5.71838 -5.57526,2.70752 -0.7237,-0.31653 -10.09491,6.4787 -4.84241,3.36487 -6.38282,7.88489 -6.10981,13.58746 0.87366,4.36475 3.0177,8.49067 5.12397,12.37928 3.31555,3.61602 8.35711,3.47393 12.84735,4.63269 7.5047,2.72229 15.45941,3.78631 23.12298,5.90161 8.39496,2.86273 17.04114,4.79321 25.56159,7.21405 9.4082,2.55597 18.8667,4.46356 28.21777,7.23462 10.09741,2.41253 10.02948,-0.38501 10.30902,-0.59967 3.51807,-4.58789 8.26727,-7.44818 13.1579,-10.40479 4.65668,-4.05054 9.46002,-7.63275 15.29901,-9.8844 13.36883,-5.81579 1.94427,1.0672 8.0188,-3.65619 4.16461,-3.49304 4.53723,-8.3081 4.70648,-13.40783 -0.35394,-6.12043 -0.76898,-12.43134 -3.2904,-18.09088 -1.73462,-3.99408 -4.94379,-3.64856 -8.69751,-3.84382 -7.53583,-2.80133 -68.67304,-13.70123 -74.63105,-13.54077 L 785,559.83923 c 5.97217,0.15491 66.97461,11.50037 74.80932,13.48377 4.01545,0.0941 7.22394,0.13361 8.89332,4.40741 2.29272,5.86572 2.91674,12.11389 3.30859,18.36694 -0.0449,5.1911 -0.25818,10.09705 -4.39545,13.78076 -5.23028,4.23774 -11.16266,7.22742 -17.57715,9.20075 -8.15661,3.53632 7.59412,-4.9707 -5.87591,4.48816 -4.85547,2.92279 -9.64478,5.68597 -13.08069,10.30005 -6.18982,4.99927 -12.19733,8.2575 -19.93225,5.71789 -9.1405,-3.04522 -18.47815,-5.24102 -27.84814,-7.46038 -8.64234,-2.25251 -17.28565,-4.41669 -25.82972,-7.02253 -7.73596,-1.83044 -15.45721,-3.72601 -23.10034,-5.92028 -4.8125,-1.05677 -9.93365,-0.68604 -13.35779,-4.78162 -2.09857,-4.02716 -4.41681,-8.17017 -5.11816,-12.70673 -0.33386,-5.83044 0.88824,-10.55578 5.88098,-14.08178 6.0733,-4.56531 12.46198,-8.9032 19.4682,-11.89014 5.39014,-2.14545 10.9726,-3.75366 16.43421,-5.7049 4.36474,-1.33569 8.73638,-2.64257 13.23645,-3.45416 9.87652,-2.63464 0.99285,0.84265 8.09155,-5.00305 l 5.41455,-2.75891 z"
- id="path12983"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 816.90289,640.87769 c -1.5556,-9.10474 -0.11987,-18.51215 1.16736,-27.58643 l 4.47784,-2.19354 c -1.51971,8.66717 -3.14044,18.07446 -0.479,26.61407 l -5.1662,3.1659 z"
- id="path12989"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 814.00702,612.10278 c -0.73322,-4.64172 -4.54669,-7.10681 -8.65253,-8.7702 -5.18811,-1.70923 -10.64197,-2.48755 -16.03045,-3.31262 -6.03809,-0.94824 -12.11371,-1.65857 -18.18805,-2.32562 -6.40076,-0.66999 -12.78199,-1.85419 -19.09558,-3.15223 -7.83857,-1.70001 -15.02466,-5.3753 -22.66687,-7.698 -5.27387,-1.72955 -13.14167,2.84839 -6.48542,-0.60266 l -5.24249,2.09967 c 5.59674,-3.45465 9.586,-6.42187 16.21466,-4.57049 7.54565,2.37256 14.56128,6.32367 22.37683,7.84833 6.43939,1.1148 12.91315,2.15277 19.40222,2.87304 6.04462,0.70197 12.06079,1.51648 18.07947,2.42511 5.46338,0.78186 11.00006,1.39582 16.20386,3.33838 4.29382,1.82965 7.94476,4.52552 9.4989,9.08838 l -5.41455,2.75891 z"
- id="path12991"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 816.86414,616.38849 c 0.63293,-9.94824 10.79504,-16.30951 18.44555,-21.27411 5.77753,-4.2774 31.82862,-14.68634 26.9018,-12.30121 l 5.10479,-2.0299 c -8.52099,5.28118 -23.2052,10.75109 -27.73071,12.6676 -6.95819,4.37994 -17.96545,10.78778 -17.30682,20.17871 l -5.41462,2.75891 z"
- id="path12999"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 746.28223,596.23321 c -0.0407,3.34736 0.37542,6.6908 0.48919,10.03888 0.25519,2.31537 -0.42108,4.92267 0.18146,6.98236 4.49359,0.2381 8.79962,1.12104 13.11829,2.28827 3.15204,0.76514 6.38617,1.21808 9.45959,2.2815 3.2981,1.42096 6.74353,2.4461 10.25501,3.17444 1.97247,0.40075 3.54797,1.58252 5.25036,2.55773 -3.76647,3.8407 -1.8648,-0.15753 -1.38751,-3.20617 0.47766,-2.91449 0.42774,-5.87598 0.8197,-8.80219 0.26087,-2.38427 0.48163,-3.21557 0.53168,-5.6159 -0.39404,-1.67365 -1.89123,-1.30927 -3.37787,-1.31317 -3.02569,-0.0963 -6.60235,0.24096 -9.62213,0.0145 -4.3161,-0.42267 -9.71979,-1.29925 -14,-2.02563 -3.74853,-0.86243 -5.81439,-2.77655 -9.50146,-3.87232 -1.22388,-0.4491 -2.48212,-0.7807 -3.73639,-1.12897 l 2.32068,-1.59655 c 1.23449,0.37396 2.47271,0.73291 3.68139,1.18652 3.64624,1.12891 5.64789,3.14624 9.39264,3.89801 4.2832,0.7016 9.66724,1.70813 13.99347,2 3.08246,0.15478 6.71564,-0.10577 9.80023,-0.16907 1.63263,0.002 3.31763,-0.25091 3.60919,1.70801 -0.0906,2.42676 -0.39257,3.27881 -0.61334,5.6955 -0.40979,2.92865 -0.43115,5.88122 -0.81317,8.80749 -0.45392,3.12024 -0.71387,5.13544 -3.54571,6.00989 -1.63343,-1.01453 -3.14533,-2.1944 -5.09296,-2.56226 -3.49109,-0.81793 -6.85864,-2.05548 -10.24536,-3.21905 -3.03229,-0.9801 -6.18628,-1.47229 -9.23096,-2.4126 -4.43072,-1.21521 -8.95923,-2.18042 -13.55865,-2.08203 -0.85279,-2.26111 0.32183,-4.92761 -0.0209,-7.40106 -0.12329,-3.29029 -0.31842,-6.61402 -0.86371,-9.85669 l 2.70728,-1.3794 z"
- id="path13001"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 801.1568,611.70172 c -2.14478,-0.71234 -8.17536,2.45337 -4.11786,0.18219 -2.53925,1.5976 -3.03552,3.82916 -2.98529,6.66748 0.1731,3.03247 2.34637,4.67835 4.94769,5.70575 5.78937,1.67316 5.89325,0.44806 6.76367,-1.16827 1.46698,-3.9325 0.41974,-7.00183 -2.82092,-9.4466 -0.73437,-0.42712 -1.51166,-0.77795 -2.27435,-1.15283 l 2.36493,-1.55633 c 0.75311,0.39276 1.52472,0.76007 2.22808,1.23858 3.2627,2.62177 4.24671,5.73053 2.91028,9.81171 -2.13916,4.60748 -7.11322,5.37018 -11.42346,3.79382 -2.73675,-0.97345 -4.88458,-2.88476 -5.159,-5.95361 -9.7e-4,-2.90003 0.25092,-5.22467 2.86377,-6.93103 2.96814,-1.75427 5.63495,-3.09534 9.16059,-2.97681 l -2.45813,1.78595 z"
- id="path17643"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 799.00494,614.14203 c 2.00074,-0.14325 2.99909,1.29303 4.04071,2.80224 0.86109,1.5221 1.43268,2.8368 0.56537,4.35236 -2.03088,1.84418 -3.68957,2.04211 -6.13794,1.64874 -1.56866,-1.25946 -2.26446,-2.93249 -1.4718,-4.71801 0.64063,-0.3584 1.28119,-0.71686 1.92176,-1.07526 l -1.52747,1.20917 c 0.47369,-0.28534 0.94739,-0.57062 1.42108,-0.85596 -0.7868,1.6355 -0.44116,3.17645 1.24994,4.37793 2.12811,0.24377 4.56604,0.0857 2.77069,0.17651 0.81067,-1.47937 0.46765,-2.63037 -0.42151,-4.17291 -1.03387,-1.55603 -2.03387,-2.82123 -4.04956,-2.5542 l 1.63873,-1.19061 z"
- id="path17647"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 727.97388,595.39166 c -0.0826,2.03424 -0.50043,4.08216 0.82013,5.68976 2.59509,1.08997 5.04913,0.64752 3.10839,0.81854 0.46881,-2.3656 1.36194,-5.15307 -1.39282,-5.9273 l 1.4419,-1.00824 c 2.89068,0.83325 2.02777,3.57086 1.66699,6.13879 -2.2356,1.9425 -3.55548,2.33838 -6.42059,0.98608 -1.3454,-1.66467 -0.90418,-3.6726 -1.02887,-5.77801 l 1.80487,-0.91962 z"
- id="path17665"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer13"
- display="none"
- style="display:none">
- <g
- id="g29628"
- transform="matrix(0.748816,0,0,0.748816,-914.0192,-717.5822)">
- <path
- d="m 560.40717,535.0556 c -0.81202,1.33545 -6.30042,5.15021 -10,7 -4.65839,2.32923 -8.62067,1.08045 -13,4 -3.83234,2.55487 -10.31464,1.29114 -14.57142,4 -5.00341,3.18402 -9.89611,3.1911 -14.42861,6.42859 -4.77475,3.41052 -8.51883,3.43994 -10.57142,8.57141 -1.29934,3.24842 -1.42858,7.15125 -1.42858,11.42859 0,2.34064 6.72482,10.8479 8.57144,12.57141 3.54651,3.31012 9.72833,4 15.42859,4 5.28656,0 12.64544,0.65186 16,3 4.17584,2.9231 12.20697,4 17.57141,4 1.15173,0 2.2857,0.28571 3.42859,0.42859 1.08178,0.13526 9.00085,3.57202 10.42859,4.57141 0.4002,0.28015 0.71423,0.66669 1.07141,1 3.73242,3.48365 9.5,5 15.5,8.5 5.74084,5.74085 12.14172,-1.64227 15,-3.07141 4.21844,-2.10925 7.29156,-3.9815 10,-7.42859 1.95208,-2.48449 7.10553,-4.10553 9.5,-6.5 3.83917,-3.83917 10.50451,-6.50225 14.5,-8.5 3.98742,-1.99371 7,-9.78448 7,-14 0,-5.26251 2.45373,-10.06604 -1,-15 -4.14466,-5.92083 -0.75782,-4.57391 -6.57141,-10 -3.55909,-3.32177 -12.00794,-2 -16.42859,-2 -7.54261,0 -13.73877,0.13062 -20,-3 -6.23224,-3.11609 -11.06153,-3 -18,-3 -5.92011,0 -9.3711,1.41931 -14.5,-2 -3.32087,-2.21386 -5.53687,-2.98248 -8.92859,-4"
- id="path21744"
- inkscape:connector-curvature="0"
- style="fill:#aeaeae;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 642.40717,557.0556 c -3.98493,2.04853 -6.87482,1.91657 -10,4 -3.44294,2.29529 -7.29456,3.14728 -11,5 -4.05774,2.02887 -8.51947,4.25977 -12,6 -2.81257,1.40625 -7.1496,4.22443 -9,7 -1.85138,2.7771 -1.22156,7.88623 -2,11 -0.91608,3.66431 0,6.3465 0,10 0,4.03534 0.47485,6.94977 2,10 0.65429,1.30866 7.92712,-3.85431 9,-6 0.67089,-1.34185 11.67492,-9 13,-9 2.45373,0 12.74182,-7.11273 14,-9 2.78259,-4.17389 4,-6.4837 4,-12 0,-4.79571 -0.42859,-9.70337 -0.42859,-15 0,-0.74353 -0.7226,-1.01159 -1.07141,-1.5"
- id="path21746"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient29649);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 592.83575,582.5556 c 0,-0.92102 -7.79162,-4.45825 -9.42859,-5.5 -5.18859,-3.30182 -9.55004,-3 -16,-3 -3.96759,0 -10.03729,-0.50927 -14,-1.5 -6.55273,-1.63818 -16.44244,0.39032 -22,-3.5 -3.40014,-2.38006 -7.88629,-2.52038 -11.42858,-5 -3.19104,-2.2337 -9.57145,0.11377 -9.57145,-5 0,-4.46014 7.56076,-4.69159 11.00003,-5.42853 5.04438,-1.08099 10.55896,-5.29431 14,-8.57147 3.49341,-3.32708 11.88965,-1.94482 16,-4 4.47864,-2.23932 8.06519,-4.4674 13,-2 6.08381,3.04193 15.21442,3 22,3 1.33332,0 2.66663,0 4,0 4.71369,0 9.0044,3 14.57142,3 6.66949,0 10.42706,6.42859 19.42858,6.42859 1.01795,0 2,0.38098 3,0.57141 3.85468,0.73426 6.12195,3.24042 7,6 0.26886,0.84503 -12.83709,1.91858 -15,3 -3.34417,1.67206 -11.57141,9.86109 -11.57141,14 0,4.6258 -9.69976,5.62958 -13.42859,6.42859 -0.61993,0.13282 -1.0476,0.7143 -1.57141,1.07141 z"
- id="path21748"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient29651);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 587.40717,613.0556 c -1.84375,0.39508 3.15673,-2.31341 4,-4 1.68939,-3.37872 -1.46351,-3.90234 2,-6.5 4.53424,-3.40063 -0.51294,-14.01294 -3,-16.5 -3.44904,-3.44897 -5.06482,-6.24536 -9,-9 -3.85578,-2.69897 -13.41767,-3 -18,-3 -7.26636,0 -14.81293,0.12476 -21,-4 -4.47284,-2.98187 -13.10224,0.59851 -18.5,-3 -3.45215,-2.30145 -13.16669,-4 -17.50004,-4 -5.56564,0 -7.11846,2.7804 -6,8 0.90235,4.211 -3.07251,7.0578 -1.5,12 1.87922,5.90601 6.37507,7.83338 12.5,9 7.34739,1.39954 16.7565,0.17102 22.50004,4 5.08136,3.38764 10.58099,2.28113 16.07141,1 5.21856,-1.21765 10.12561,2.38111 12.42859,6 2.99493,4.70642 10.80749,0.55469 14,5.57147 1.32226,2.07782 8.78173,3.3194 11,4.42853 z"
- id="path21750"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient29653);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 556.59558,647.77051 c -3.05237,0.29095 -9.40423,-1.63739 -12.00171,-6.00086 -1.14563,-1.9245 -10.00012,-1.99322 -11.40912,-2.40033 -5.15033,-1.48822 -8.42218,-4.80072 -13.79449,-4.80072 -5.07593,0 -9.92688,-4.80066 -14.99896,-4.80066 -5.13379,0 -11.56986,0.22126 -16.20545,-1.69671 -3.42532,-1.4173 -5.64438,-3.2652 -7.20102,-6.06183 -1.89273,-3.40039 1.77857,-8.42206 3.6005,-10.24402 3.49011,-3.49011 3.39395,-8.19458 7.20105,-12.00171 3.12372,-3.12372 9.33539,-0.26593 12.00168,2.40033 1.5574,1.55744 12.10458,1.3031 14.40206,3.60053 2.88947,2.88946 13.05567,2.49548 16.80237,4.12133 9.16584,3.97754 21.7605,1.1485 30.00427,5.48005 4.19025,2.20172 6.35547,2.91864 12.00171,4.80066 2.9989,0.99963 5.53815,4.75811 8.40119,6.00085 3.50964,1.52344 -13.17035,9.56982 -13.20191,9.60138 -1.28302,1.28302 -4.65808,2.72613 -6.00085,3.60052 -2.43018,1.58252 -5.74292,6.30957 -7.85577,8.40119 -0.41352,0.40936 -1.1637,0 -1.74555,0 z"
- id="path24898"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient29655);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 536.19269,632.16827 c 5.24737,0.62433 4.93536,-3.34222 6.00079,-7.20099 0.82703,-2.99523 2.62268,-2.96057 1.2002,-6.59491 -0.37427,-0.95629 -8.18714,-1.10644 -9.60138,-1.80633 -2.96338,-1.46649 -5.8324,-1.18121 -8.40119,-2.40033 -1.22332,-0.58057 -10.85571,-2.40033 -12.00171,-2.40033 -4.46167,0 -7.98675,-2.40033 -11.42282,-2.40033 -2.81189,0 -7.79721,-4.37537 -9.69882,-1.2002 -1.60837,2.68561 -3.69845,6.81757 -5.28207,8.40119 -1.44269,1.44275 8.59552,5.53363 9.60135,6.00085 2.59958,1.20758 6.34924,1.2002 9.60135,1.2002 4.09259,0 5.0689,2.6101 8.40121,3.60052 5.49518,1.63318 9.25598,1.711 13.74573,4.80066 0.60699,0.41773 1.23767,0.80011 1.8565,1.2002 3.19068,2.06268 3.62378,1.17688 6.00086,-1.2002 z"
- id="path25666"
- inkscape:connector-curvature="0"
- style="fill:#8e8e8e;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 557.79571,638.16913 c 0.77497,0.50098 -3.66394,-1.2002 -7.20098,-1.2002 -8.2934,0 0.94586,-8.97131 1.68463,-10.80151 0.65197,-1.61524 2.73034,-2.2251 4.31622,-2.94562 4.35394,-1.97833 5.69183,1.81043 9.60132,2.94562 5.66498,1.64502 -0.39606,5.64062 -1.20014,7.20105 -1.4367,2.78784 -4.54766,4.80066 -7.20105,4.80066 z"
- id="path25668"
- inkscape:connector-curvature="0"
- style="fill:none;stroke:#000000;stroke-width:1.20017004px" />
- <path
- d="m 558.99591,638.67932 c -5.15729,-0.18695 -6.51837,-2.91052 -12.00171,-2.91052 -3.83581,0 4.10822,-6.50861 6.00086,-8.40119 2.68176,-2.68176 2.8775,-3.60052 7.20099,-3.60052 1.52783,0 5.90094,2.8996 6.63153,4.30414 1.08783,2.09106 -3.38153,4.781 -4.23121,6.49737 -0.81622,1.64887 -2.2088,2.7049 -3.60046,4.11072 z"
- id="path26430"
- inkscape:connector-curvature="0"
- style="fill:#8e8e8e;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 579,593.09448 c 3.05072,-0.60229 2.4754,-3.82202 4,-6 0.15356,-0.21942 -7.15204,-2.38403 -7.5,-2.5 -4.43683,-1.47894 -8.19196,-0.5 -12.5,-0.5 -4.05365,0 -7.02698,-2 -11,-2 -2.2912,0 -0.73224,5 3,5 3.14288,0 6.28571,0 9.42859,0 3.70648,0 5.05878,1.77667 7.57141,3.57141 1.24628,0.8902 7.13751,2.33692 8.5,1.42859"
- id="path28833"
- inkscape:connector-curvature="0"
- style="fill:#8e8e8e;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 507,578.09448 c 2.72229,0.30145 3.64179,-1.42004 2,-4 -2.26199,-3.55456 -5,0.29254 -5,3 0,1.05408 2.12292,0.41529 3,1 z"
- id="path29593"
- inkscape:connector-curvature="0"
- style="fill:#6d6d6d;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 559.27478,533.57196 c -3.87299,5.67157 -11.16852,9.52594 -17.92267,11.11737 -4.48321,0.81299 -8.79577,2.27405 -13.18341,3.45825 -5.41113,1.99781 -11.00024,3.52784 -16.33462,5.71839 -5.57526,2.70746 -0.7237,-0.31659 -10.09488,6.4787 -4.84241,3.3648 -6.38282,7.88482 -6.10981,13.58746 0.87369,4.36468 3.0177,8.4906 5.12394,12.37921 3.31555,3.61603 8.35714,3.47394 12.84738,4.63269 7.5047,2.72235 15.45941,3.78632 23.12298,5.90161 8.3949,2.86279 17.04114,4.79328 25.56159,7.21411 9.4082,2.55591 18.8667,4.4635 28.21777,7.23456 10.09741,2.41254 10.02948,-0.38501 10.30902,-0.59961 3.51807,-4.58795 8.26728,-7.44824 13.1579,-10.40484 4.65662,-4.05048 9.46002,-7.63276 15.29895,-9.8844 13.3689,-5.81574 1.94434,1.0672 8.0188,-3.65619 4.16467,-3.49304 4.53729,-8.30811 4.70654,-13.40784 -0.354,-6.12042 -0.76898,-12.43133 -3.2904,-18.09082 -1.73468,-3.99414 -4.94379,-3.64862 -8.69751,-3.84381 -7.53583,-2.80139 -68.67304,-13.70123 -74.63104,-13.54083 l 4.4779,-3.25519 c 5.97217,0.15491 66.97461,11.5003 74.80933,13.4837 4.01538,0.0941 7.22393,0.13361 8.89331,4.40741 2.29272,5.86573 2.91675,12.11389 3.30859,18.36701 -0.0449,5.19104 -0.25818,10.09698 -4.39545,13.78076 -5.23028,4.23767 -11.16265,7.22741 -17.57714,9.20068 -8.15662,3.53632 7.59411,-4.97064 -5.87592,4.48822 -4.85547,2.92273 -9.64477,5.68598 -13.08069,10.30005 -6.18982,4.99927 -12.19739,8.25751 -19.93231,5.7179 -9.14044,-3.04523 -18.47815,-5.24109 -27.84808,-7.46045 -8.64234,-2.2525 -17.28565,-4.41669 -25.82971,-7.02252 -7.73597,-1.83045 -15.45722,-3.72602 -23.10038,-5.92029 -4.81246,-1.0567 -9.93365,-0.68604 -13.35778,-4.78162 -2.09855,-4.02716 -4.41681,-8.17016 -5.11817,-12.70672 -0.33386,-5.83045 0.88828,-10.55579 5.88098,-14.08179 6.07334,-4.56531 12.46201,-8.90314 19.46823,-11.89014 5.39008,-2.14545 10.9726,-3.75366 16.43421,-5.70483 4.36474,-1.33576 8.73639,-2.64264 13.23645,-3.45423 9.87652,-2.63464 0.99286,0.84266 8.09149,-5.00305 l 5.41461,-2.75891 z"
- id="path21768"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 591.75604,615.64917 c -1.55554,-9.10468 -0.11981,-18.51215 1.16742,-27.58643 l 4.47778,-2.19354 c -1.51965,8.66724 -3.14044,18.07447 -0.47894,26.61414 l -5.16626,3.16583 z"
- id="path21770"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 588.86017,586.87427 c -0.73316,-4.64166 -4.54663,-7.10675 -8.65247,-8.77014 -5.18811,-1.70929 -10.64197,-2.48755 -16.03052,-3.31269 -6.03808,-0.94824 -12.11364,-1.65857 -18.18798,-2.32562 -6.40076,-0.66998 -12.78199,-1.85419 -19.09559,-3.15222 -7.83856,-1.70001 -15.02465,-5.37531 -22.6669,-7.698 -5.27386,-1.72955 -13.14166,2.84845 -6.48538,-0.60266 l -5.24252,2.09967 c 5.5968,-3.45465 9.58603,-6.42188 16.21472,-4.57044 7.54556,2.3725 14.56125,6.32361 22.3768,7.84827 6.43939,1.11481 12.91315,2.15277 19.40222,2.87305 6.04456,0.70196 12.0608,1.51648 18.07947,2.42517 5.46338,0.78186 11,1.39575 16.20386,3.33832 4.29382,1.82965 7.9447,4.52551 9.4989,9.08844 l -5.41461,2.75885 z"
- id="path21772"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 591.71735,591.15997 c 0.63287,-9.94818 10.79504,-16.30951 18.44549,-21.27411 5.77759,-4.2774 31.82868,-14.68634 26.9018,-12.3012 l 5.10485,-2.02991 c -8.52099,5.28125 -23.2052,10.7511 -27.73071,12.6676 -6.95819,4.37995 -17.96545,10.78778 -17.30682,20.17877 l -5.41461,2.75885 z"
- id="path21774"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 490.58621,599.05914 c -1.38989,2.58875 -3.96548,6.8216 -5.26398,9.45063 -1.14361,3.0465 -2.62017,5.94451 -4.3374,8.70282 -1.88492,2.71856 -2.49887,4.23077 -0.0953,6.51141 3.70886,2.11285 7.91561,2.91174 12.00466,3.9552 4.42514,0.96759 8.91718,2.02539 13.08793,3.82086 4.20996,1.96832 8.60342,3.42108 13.06424,4.69385 4.37402,1.03986 8.81866,1.78131 13.28497,2.26471 3.01776,0.39319 5.91162,1.3172 8.82733,2.15356 3.75733,1.51087 7.3067,3.44519 11.08191,4.9159 2.35675,1.09491 4.80383,1.15448 7.34967,1.18634 7.19684,-2.7519 0.93188,0.0914 -0.34778,0.79608 -0.67041,0.36926 4.65082,-4.93726 6.03784,-6.70215 2.43293,-3.69934 6.76892,-5.55945 10.78943,-7.14355 12.18573,-5.72467 -1.86053,1.86639 7.43622,-3.4762 -6.10528,4.48981 -1.41205,-0.0784 -0.50592,-3.38648 0.59076,-2.71099 -1.65991,-3.29345 -3.8407,-4.29425 -3.5169,-0.92584 -6.95636,-1.87078 -10.38806,-3.09527 -5.68176,-1.79889 -11.4472,-3.33692 -17.22571,-4.79071 -6.31823,-1.12042 -12.44189,-3.04224 -18.63079,-4.68445 -4.96991,-1.34607 -9.95124,-2.65594 -14.94208,-3.92023 -4.28534,-1.16815 -8.57386,-2.37317 -12.85526,-3.5783 -3.77023,-0.85077 -7.53943,-1.7071 -11.30304,-2.58466 -1.96835,-0.33478 -3.74167,-1.20837 -5.54709,-2.00305 l 4.02979,-2.65796 c 1.73523,0.79816 3.45102,1.65539 5.34576,2.02197 3.79865,0.78632 7.54538,1.78125 11.34329,2.57092 4.22861,1.34382 8.5192,2.47449 12.80912,3.59577 4.94641,1.39203 9.89618,2.77606 14.89728,3.96197 6.1181,1.66687 12.08892,3.93305 18.40942,4.81134 5.86548,1.36481 11.66717,3.01282 17.45294,4.68177 3.50342,1.14203 7.04035,2.03302 10.57446,3.03772 2.36603,1.07263 4.87909,1.84869 4.2569,4.86047 -1.19409,4.48041 -2.9505,6.37067 -7.61444,8.46521 8.57166,-4.98138 -3.16272,3.078 -6.37384,3.76856 -6.3537,2.63391 2.37054,-3.4632 -3.6969,1.90722 -1.32971,1.84796 -7.10467,10.09448 -13.98645,10.65235 -2.59796,-0.0438 -5.07831,-0.14496 -7.44702,-1.35755 -3.67273,-1.62445 -7.18854,-3.5954 -10.99622,-4.90094 -2.90887,-0.81689 -5.8208,-1.61505 -8.80755,-2.09424 -4.4112,-0.50225 -8.76416,-1.40539 -13.0763,-2.4671 -4.55221,-1.13904 -8.98825,-2.6582 -13.24411,-4.64734 -4.16726,-1.75769 -8.59918,-2.7644 -12.99182,-3.79797 -4.18121,-1.02783 -8.50555,-1.8938 -12.31106,-3.97449 -2.60706,-2.48632 -2.25867,-4.19806 -0.146,-7.18939 1.72614,-2.68707 3.19892,-5.5578 4.22238,-8.58441 1.2966,-2.68451 3.97077,-6.9148 5.3841,-9.55121 0,0 5.87253,-4.84924 4.28521,-1.90472 z"
- id="path24892"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 490.84497,605.8266 c 4.52383,-3.35272 -0.77484,2.18164 -1.66873,4.35522 -0.59018,1.88215 -1.33658,3.70997 -2.11523,5.51783 -0.61258,2.33374 1.53686,2.85247 3.37387,3.59802 2.00991,1.20392 4.26263,1.80792 6.44613,2.58948 3.20682,0.9469 6.52124,1.41882 9.77045,2.20733 3.07696,1.03699 6.22146,1.93439 9.21408,3.20893 2.54871,1.15411 5.29901,1.47491 8.01148,1.97448 2.00476,0.47425 3.97692,1.04712 5.93475,1.67536 1.70459,0.74151 3.48633,1.23187 5.30792,1.56781 1.17657,0.051 2.38879,0.2655 3.53302,-0.0132 1.45856,-1.83136 4.10925,-4.12561 4.69885,-6.34045 0.74976,-1.73663 1.50043,-3.27918 1.66266,-5.14929 -0.6289,-1.85779 -4.5661,-2.31531 -6.24322,-2.96393 -2.58789,-0.95746 -6.22083,-1.22901 -8.79151,-2.2312 -3.3537,-1.47827 -6.97918,-2.10883 -10.58923,-2.57862 -3.91693,-0.55468 -6.85877,-1.52429 -10.73993,-2.28857 -3.88519,-0.57513 -7.68051,-1.68091 -11.3201,-3.14252 -1.68078,-0.53302 -3.37549,-1.0133 -5.09784,-1.38769 l 2.14694,-1.49103 c 1.70011,0.43518 3.38443,0.93152 5.05771,1.46191 3.63828,1.43744 7.38614,2.54395 11.26166,3.13196 3.89115,0.77863 6.86185,1.68756 10.79215,2.23694 3.62323,0.47992 7.25457,1.18463 10.63287,2.61829 2.60315,0.96704 6.24457,1.27069 8.86963,2.18237 1.83063,0.67895 5.78058,1.08874 6.40625,3.20691 -0.0942,1.93176 -0.76056,3.48419 -1.60559,5.24206 -0.59778,2.23236 -3.30664,4.54029 -4.67841,6.44385 -2.83026,1.93585 -4.7536,3.59705 -8.20843,2.60053 -1.83362,-0.34766 -3.61225,-0.87323 -5.34632,-1.57618 -1.93487,-0.63336 -3.87323,-1.26483 -5.87274,-1.67535 -2.70739,-0.50281 -5.43896,-0.90802 -7.96924,-2.07587 -2.98468,-1.26928 -6.12213,-2.10327 -9.16851,-3.19183 -3.28287,-0.7749 -6.61969,-1.28162 -9.88092,-2.14752 -2.19669,-0.77112 -4.41394,-1.4682 -6.45127,-2.62787 -1.98037,-0.8078 -4.17062,-1.39313 -3.61581,-3.90369 0.77866,-1.82605 1.56903,-3.66473 2.17383,-5.56714 1.28439,-3.19214 3.12726,-5.70776 6.3634,-7.13452 l -2.29462,1.66718 z"
- id="path24894"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 554.28253,623.8291 c 2.44715,-1.91656 -0.54242,0.81781 -1.84003,3.18915 -0.78033,1.47357 -1.65484,2.85645 -2.67157,4.17035 -1.37023,1.71564 -2.04064,2.37469 -0.85809,3.78284 2.07446,0.36456 4.09705,0.47992 6.07233,1.22693 1.80243,0.80487 3.64233,1.43853 5.56732,1.86688 -0.698,0.77148 0.13983,0.22394 2.82648,-2.59918 1.87671,-1.6554 3.20129,-3.78693 4.66974,-5.78669 1.26928,-2.21563 -0.46088,-2.49127 -2.17292,-3.36072 -2.13861,-1.44085 -4.56665,-2.10284 -7.00439,-2.82336 -1.8523,-0.78985 -3.66242,-0.98138 -5.59882,-0.79773 l 2.13086,-1.64075 c 1.95892,-0.0793 3.78851,0.17298 5.64252,0.95191 2.46692,0.7099 4.86194,1.44232 7.0437,2.84985 1.92926,0.84253 3.66998,1.34919 2.39679,3.76026 -1.47778,1.99121 -2.7879,4.15674 -4.65405,5.82135 -2.1225,2.28143 -4.42127,4.48315 -7.51258,5.17529 -1.90979,-0.50836 -3.78186,-1.09222 -5.58136,-1.91913 -1.96917,-0.72094 -4.00427,-0.82855 -6.06842,-1.18176 -1.89557,-1.33929 -0.59411,-2.62 0.6214,-4.2641 1.04224,-1.30029 1.89099,-2.69757 2.70233,-4.15515 1.5904,-2.83392 3.38538,-4.77411 6.58338,-5.93335 l -2.29462,1.66711 z"
- id="path24896"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 553.91467,580.13007 c 6.57117,3.47863 14.25666,3.45599 21.50525,4.0487 3.90064,0.71137 9.73773,-1.08038 8.9386,4.37708 -0.69519,3.6015 -1.849,3.92889 -5.31085,5.40271 -4.84515,-0.73969 -9.31677,-2.91809 -14.10712,-3.98267 -3.87542,-1.07599 -7.88879,-0.79431 -11.83966,-1.14233 -3.85492,-0.0983 -3.61566,-4.53681 -1.97766,-6.98786 l 2.55029,-1.08252 c -1.75647,2.25904 -2.1441,6.37317 1.60199,6.51545 3.92463,0.19055 7.85694,0.30767 11.69715,1.28747 4.82879,1.06616 9.3612,3.46625 14.32318,3.72888 -1.20935,1.09516 -0.97699,1.87348 0.59491,-2.53729 0.81372,-4.93719 -4.63129,-3.49798 -8.19965,-4.35175 -7.43713,-1.05615 -15.09051,-1.08374 -22.23462,-3.48992 l 2.45819,-1.78595 z"
- id="path28825"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 508.45126,577.64624 c 0.13367,-5.18738 -4.45712,-4.0293 -1.99542,-4.72589 -1.4776,2.90796 0.34006,4.41034 3.10007,4.6911 0.11609,5.5e-4 0.23221,10e-4 0.34829,0.002 l -2.34701,1.69299 c -0.10348,-0.007 -0.20694,-0.014 -0.31039,-0.0209 -2.91663,-0.51697 -4.72953,-2.17218 -3.29184,-5.2663 4.39911,-3.39581 5.88809,-3.14477 7.16522,2.26752 l -2.66892,1.35986 z"
- id="path28831"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer14"
- display="none"
- style="display:none">
- <g
- id="g28809"
- transform="matrix(0.748816,0,0,0.748816,-802.8434,-658.0415)">
- <path
- d="m 449.24966,468.67096 c -0.53341,3.49249 1.01651,4.96008 2,7 0.54859,1.13785 4.53113,4 -1,4 -1.44082,0 -4.31353,3 -7,3 -0.94613,0 -4.76675,2 -8,2 -2.7738,0 -3.84308,1.27023 -7,-0.5889 -0.31054,-0.18289 -0.66668,-0.27405 -1,-0.4111 -1.94076,-0.79785 -7.90289,-2.78687 -8,-3 -0.006,-0.0128 -8.25488,1 -12,1 -2.5166,0 -10.93103,1.30099 -12,-1 -0.44412,-0.95596 -2.25464,-0.25464 -3,-1 -0.33334,-0.33335 -0.66668,-0.66666 -1,-1 -2.03823,-2.03824 3.0459,-3.01962 4,-3.42774 3.99665,-1.7095 4.78382,-2.86236 8,-4.57226 2.70871,-1.4401 5.15873,-2.767 3.57624,-6.5394 -0.89658,-2.13733 -7.49012,-2.02032 -9.57624,-2.02032 -7.53817,0 -12.96865,-5.44028 -20,-5.44028 -1.63537,0 1.07279,-12.6918 1.54453,-14 1.71857,-4.76608 0.0281,-10.43671 2.02029,-15.40997 1.62924,-4.06714 2.43518,-9.08164 2.43518,-13.59003 0,-7.4408 0.59528,-15.37909 0.59528,-23 0,-7.28272 2.03894,-5.63422 5.40472,-9 3.10553,-3.10553 13.40061,-1.79968 17,0 2.31409,1.15704 9.81592,1 13,1 3.43225,0 10.82267,0.11267 14,2 4.29605,2.55188 12.15192,0.80298 17,0 4.27195,-0.70755 8.16632,0.90033 12,2 2.97171,0.85242 7.79645,-0.5253 10,0.43646 2.63196,1.14874 3.6474,2.00922 4.5293,4.56354 0.63788,1.8475 1.4707,4.34527 1.4707,7 0,4.14252 0,8.28506 0,12.42758 0,3.94424 -2,7.13165 -2,11.11166 0,5.15033 -3,9.41782 -3,14.46076 0,3.61365 -1,8.02017 -1,12 0,3.66665 0,7.33334 0,11 0,3.24362 -1.51101,4.55896 -2.54178,6.48089 -0.71701,1.33695 -3.69061,2.51911 -5.45822,2.51911 -1.47769,0 -5.98819,1.44354 -9,0.51135 -1.55331,-0.48077 -5.18716,-4.12918 -6.50241,-1.51135 -0.16748,0.33334 0.33493,0.66665 0.50241,1 z"
- id="path26472"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient29595);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 448.84494,468.08206 c -0.33334,0.5004 -0.66666,1.00079 -1,1.50119 -0.0899,0.13498 3,4.92719 3,6.06092 0,1.82189 4.47806,4.43789 0,4.43789 -3.57498,0 -10.98584,2.98584 -13,5 -2.38654,2.38654 -8.31811,-1.31814 -9,-2 -2.38055,-2.38055 -8.28644,-2 -12,-2 -4.33334,0 -8.66666,0 -13,0 -5.05783,0 -6.66214,-1.04748 -10,-2.40744 -6.0741,-2.47476 1.48947,-3.01623 5,-4.04059 5.05902,-1.47622 6.29669,-4.64559 8.5661,-8.08123 0.0791,-0.11972 17.94751,-0.47074 20.96066,-0.47074 4.95563,0 9.5557,0.47074 14.47324,0.47074 2.78677,0 6,0.53647 6,3.52926"
- id="path27249"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient29597);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 395.84494,387.50836 c -2.30768,-0.24103 -3,1.95056 -3,5.05078 0,3.78845 0,7.55246 0,11.52292 0,2.75833 -0.1626,11.5958 -1.59634,14 -1.96946,3.30255 0.32763,9.26871 -2.40366,12 -1.09454,1.09454 -1,9.60562 -1,12 0,4.79663 -0.23325,6 5,6 4.18951,0 13.29175,0.48111 17,2.56085 3.78665,2.12369 13.1539,2.43915 17.5166,2.43915 5.5994,0 12.13892,-0.38821 17.4834,1 3.01688,0.78363 7.51788,2.3707 7.51788,-2 0,-5.33969 0.27075,-11.21155 2.48212,-16 1.82871,-3.95984 2,-10.98276 2,-15.49121 0,-6.64389 2,-12.5105 2,-19.50879 0,-5.52292 0.74921,-6.73431 -4,-9 -0.62271,-0.29706 -1.33334,-0.35538 -2,-0.53308 -1.10666,-0.29498 -8.86069,1.53308 -12,1.53308 -4.33289,0 -7.64102,-2 -12,-2 -2.22064,0 -12.39301,-0.26599 -14,-1 -3.92325,-1.79199 -10.58142,-2 -15,-2 -1.50284,0 -1.71494,-0.12442 -3,0.43646"
- id="path28783"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient29599);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 388.97531,376.74646 c -3.58917,5.49039 -3.44373,13.7442 -3.4519,20.82266 0.44494,6.18036 -0.71149,12.23548 -1.75049,18.29352 -0.8981,4.53986 -1.20612,9.09409 -1.26825,13.71066 0.36328,5.74756 -0.79736,11.30414 -2.15238,16.85001 -1.03659,3.16998 -1.71777,6.40396 -2.20639,9.68176 l -4.29126,2.15695 c 0.51551,-3.32397 1.15,-6.63183 2.29538,-9.82376 1.35636,-5.47668 2.41349,-10.98837 2.02619,-16.66143 0.0191,-4.65119 0.37524,-9.24357 1.29526,-13.81162 1.11255,-6.05826 2.29254,-12.11887 1.96478,-18.31342 -0.009,-7.08673 -0.44241,-18.97757 2.85181,-20.78469 l 4.68725,-2.12064 z"
- id="path26444"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 388.81552,377.18491 c 4.23257,0.0876 8.44156,0.14978 12.65308,0.60028 4.84359,0.99966 9.76141,1.66455 14.70608,1.84463 7.15796,0.19013 14.25293,1.28736 21.40579,1.60721 7.25666,0.26947 14.47681,1.11893 21.72748,1.48117 4.87042,0.41794 9.20038,-0.55685 14.63321,0.94779 3.4689,1.78961 6.09454,2.74783 9.46912,5.08386 3.29425,3.29422 2.19616,3.29422 1.09808,5.49038 l -4.39233,2.76194 c -0.33381,-0.72593 1.73104,-4.48545 0.49542,-5.04969 -3.26474,-2.35089 -6.6536,-4.61898 -10.66873,-5.45297 -4.85489,-0.21744 -9.6821,-0.68637 -14.52945,-0.98166 -7.18363,-0.57745 -14.35703,-1.37338 -21.56369,-1.58514 -7.19986,-0.18103 -14.29764,-1.72769 -21.52781,-1.52152 -4.97299,-0.19247 -9.92028,-0.83557 -14.79638,-1.83099 -4.29581,-0.53876 -8.5845,-0.58768 -12.90875,-0.34464 l 4.19888,-3.05065 z"
- id="path26446"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 484.62442,394.09714 c -1.6947,5.23773 -3.24048,10.46649 -3.8779,15.96295 -0.24823,5.51629 -0.5751,11.03357 -1.20547,16.52212 -0.53299,4.74964 -0.52225,9.49387 -1.10675,14.23618 -1.21924,4.58517 -2.44666,9.18146 -3.61872,13.78442 -0.70001,3.77707 -0.53909,7.63834 -0.51819,11.46146 l -4.37435,2.23001 c 0.007,-3.862 -0.14286,-7.76392 0.52356,-11.58166 1.13595,-4.59238 2.45608,-9.1384 3.61706,-13.72593 0.61142,-4.75763 0.70148,-9.52755 1.20874,-14.2984 0.66797,-5.48752 0.93131,-11.01794 1.24963,-16.53375 0.71097,-5.35428 2.13724,-10.51147 3.47797,-15.70114 l 4.62442,-2.35626 z"
- id="path26448"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 470.70682,468.08206 c 9.4798,-3.703 -6.92697,3.53794 -11.40952,3.84189 -4.1159,-1.16708 -7.90283,-2.67999 -12.23803,-3.06534 -6.97751,-0.25421 -13.96372,-0.1514 -20.94538,-0.19308 -6.7124,0.56604 -13.08615,-0.93674 -19.50339,-2.61148 -7.31417,-2.17579 -14.59649,-4.42133 -22.06222,-6.03635 -4.38678,-0.7966 -7.60446,-0.99655 -12.04419,-0.99066 l 2.59619,-2.5163 c 4.41666,0.006 8.86612,-0.007 13.21326,0.89093 7.44845,1.78064 14.82281,3.8407 22.16907,5.99903 6.32574,1.69076 12.6759,2.97802 19.28021,2.61352 7.04474,-0.0481 14.0957,-0.15848 21.13693,0.097 4.36325,0.39944 8.19174,1.86322 12.35983,2.90838 11.23203,-0.92865 3.25403,-1.16058 11.64612,-3.98822 l -4.19888,3.05066 z"
- id="path26450"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 461.21481,470.45676 c -2.82647,-3.32593 -3.00854,-7.78723 -2.55768,-11.93491 0.44425,-4.76843 1.70255,-9.37711 2.98383,-13.9725 1.26895,-4.3703 2.00497,-8.8638 2.99277,-13.29929 1.82922,-7.03253 3.06332,-14.17868 3.9432,-21.38538 0.89466,-6.7167 1.2905,-15.00207 0.79062,-21.73751 l 1.96384,-1.01889 c 0.19553,6.79995 -0.14191,15.13394 -1.02866,21.90292 -0.8504,7.19101 -1.98007,14.31537 -3.80096,21.33579 -0.89435,4.42074 -1.47519,8.90817 -2.81354,13.2315 -1.32409,4.59366 -2.63128,9.20624 -3.08688,13.98502 -0.42276,3.90963 -0.54126,8.655 2.71289,11.36792 l -2.09943,1.52533 z"
- id="path26460"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 393.17014,386.56222 c 0.47976,3.18137 0.0757,7.1604 0.0841,10.39414 0.36676,3.36191 0.4213,6.6929 0.13367,10.05588 -0.3595,3.05453 -0.98035,6.05969 -1.44565,9.09863 -0.57831,3.00501 -0.52344,6.06497 -0.82654,9.09537 -0.34363,3.40152 -1.17703,6.71298 -1.97824,10.02649 -0.63797,2.58062 -0.74988,5.25735 -1.56699,7.78744 -0.73431,1.818 -1.19152,3.13111 -0.0897,4.49921 2.09772,0.87479 6.31498,0.85312 8.53485,1.13351 2.44007,0.57483 4.85019,0.90869 7.36185,0.93698 2.60007,0.002 5.20011,-0.008 7.80023,-0.0102 2.83176,0.27512 5.63352,0.73627 8.43634,1.21891 2.44672,0.66494 4.93695,1.11398 7.42487,1.58624 2.91321,0.44372 5.84552,0.71405 8.77982,0.98825 2.50921,0.17218 5.02606,0.12485 7.53936,0.11871 2.64984,-0.0497 5.2685,0.53891 7.72123,1.49744 3.02224,0.39752 0.31555,2.18137 0.4176,-0.95032 0.15985,-2.21539 0.72015,-4.3674 1.16473,-6.53695 0.29367,-2.81479 0.39005,-5.63923 0.72605,-8.45636 0.24579,-3.97364 1.24505,-7.81955 2.13766,-11.6807 0.59339,-3.32745 1.3754,-6.59357 1.87824,-9.93518 0.62408,-3.17587 0.63907,-6.4006 0.63702,-9.62366 0.004,-2.85681 1.11774,-6.33337 0.7934,-9.17352 -0.18875,-2.10049 0.26593,-3.66605 -0.0301,-5.73804 -1.75138,-1.34332 -5.84733,-0.76373 -8.03049,-0.76217 -4.68244,0.13766 -9.34351,-0.25214 -14.004,-0.63102 -5.72018,-0.25872 -11.35092,-1.33145 -16.99557,-2.21859 -4.85437,-0.61072 -9.76209,-0.88788 -14.6536,-1.04666 -3.89764,-0.17698 -9.47299,-0.61695 -13.31845,0.0258 -0.21008,0.0431 -0.4202,0.0863 -0.63031,0.12946 l 1.34564,-1.01883 c 0.20761,-0.0403 0.41519,-0.0807 0.6228,-0.12109 3.83561,-0.59678 9.39347,-0.1337 13.27402,0.05 4.86682,0.1496 9.75031,0.46137 14.57276,1.12854 5.65951,0.87635 11.31674,1.90519 17.04647,2.1666 4.68909,0.35037 9.37088,0.77563 14.08103,0.561 2.24112,-0.0146 6.54623,-0.71966 8.23642,0.88657 0.41549,2.0831 -0.2529,3.67929 0,5.79541 0.33005,2.85306 -0.81717,6.33618 -0.81873,9.20688 -0.0111,3.22299 -0.002,6.45688 -0.6073,9.63507 -0.49383,3.36935 -1.34021,6.64285 -1.96475,9.99026 -0.86176,3.85495 -1.84091,7.69535 -2.11096,11.65137 -0.32239,2.80618 -0.34372,5.61749 -0.62097,8.41968 -0.46152,2.16293 -1.00232,4.31595 -1.1825,6.52457 -0.0335,2.31775 -0.91241,3.83764 -3.42947,2.69116 -2.43897,-0.99668 -5.02527,-1.57169 -7.67969,-1.43332 -2.48453,4.6e-4 -4.97,-0.0119 -7.44916,-0.20068 -2.95435,-0.25928 -5.91507,-0.47443 -8.84152,-0.96579 -2.49005,-0.4441 -4.9726,-0.90363 -7.40891,-1.5972 -2.80023,-0.51264 -5.61792,-0.89798 -8.45123,-1.17017 -2.59445,-8.5e-4 -5.18881,9.5e-4 -7.78323,-0.004 -2.52311,-0.0341 -4.93656,-0.37933 -7.39444,-0.93655 -2.24039,-0.28592 -6.46106,-0.2803 -8.5907,-1.13699 -1.42053,-1.35944 -0.70404,-2.92148 -0.0295,-4.7609 0.87829,-2.51587 1.0387,-5.18918 1.60815,-7.78555 0.83545,-3.30811 1.72492,-6.6113 2.03882,-10.02265 0.2468,-3.03622 0.22498,-6.08828 0.80496,-9.09353 0.43479,-3.0372 1.09269,-6.02954 1.43021,-9.08038 0.30221,-3.36316 0.3215,-6.70121 -0.0827,-10.06687 0.008,-3.12482 0.40609,-6.96979 -0.11643,-10.05212 l 1.4996,-1.08954 z"
- id="path26464"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 450.43106,464.51013 c 1.1543,2.5156 1.0163,5.46848 1.3714,8.19635 0.56198,2.54196 2.52502,3.65872 4.34485,5.11328 -3.60046,3.38144 -8.85541,5.45493 -13.64145,7.3664 -2.7514,1.04416 -5.61044,1.72036 -8.48038,2.34308 -3.17657,0.99649 -5.04828,-0.53061 -7.74106,-2.00098 -2.64962,-1.70175 -5.67816,-2.11313 -8.71048,-2.61286 -2.84429,-0.25805 -5.71463,-0.2666 -8.55258,-0.0175 -2.85299,0.47784 -5.74872,0.39312 -8.6289,0.38791 -2.71121,0.22863 -5.2612,-0.47416 -7.80472,-1.29352 -2.05478,-0.94684 -3.98865,-2.03302 -6.11737,-2.80737 -6.41333,-0.0988 1.10565,-3.15677 4.18545,-3.7024 2.7125,-0.59176 5.49909,-0.70486 8.18476,-1.37137 -3.72681,2.07831 5.4703,-3.4552 8.33288,-5.12072 -5.93448,4.45248 -1.13641,-0.35025 -0.0444,-3.34424 0.43081,-0.64646 -0.0267,-0.13083 -0.0401,-0.19626 l 4.25506,-2.10004 c -0.0524,0.14914 -0.37619,-0.1676 -0.11575,0.32926 -1.56134,4.2633 -2.94833,6.14181 -7.54669,8.13818 6.99295,-4.08047 -2.92319,2.0079 -8.38422,5.02029 -2.68701,0.53611 -5.43207,0.75528 -8.1325,1.27478 -5.57502,1.11972 0.58248,-2.92453 3.04553,-1.46026 2.10751,0.80981 4.0393,1.92126 6.10812,2.81189 2.49472,0.82288 4.99875,1.42322 7.65909,1.21756 2.85974,-0.003 5.73325,0.0329 8.56699,-0.41486 2.87258,-0.20395 5.77829,-0.27008 8.64572,0.10007 3.06454,0.52853 6.0996,1.01505 8.78179,2.7269 2.64637,1.50696 4.51496,2.69031 7.61475,1.74676 2.83426,-0.64318 5.66913,-1.30139 8.37717,-2.38183 3.25824,-1.23877 12.06326,-5.4635 6.15246,-2.36719 -1.84536,-1.40076 -3.6456,-2.82352 -4.27148,-5.27921 -0.34784,-2.46197 -0.0522,-5.51767 -1.23123,-7.52862 l 3.81735,-2.77347 z"
- id="path26468"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer9"
- display="none"
- style="display:none">
- <g
- id="g21501"
- transform="matrix(1.596124,0,0,1.197575,-849.8943,-968.111)">
- <path
- d="m 112.04889,577.52356 c 0,0.61127 0.39405,3.56299 0.39405,5.44958 0,1.92048 0.39404,3.52344 0.39404,5.44953 0,1.96576 0.71684,4.91955 1.57617,6.61737 1.25511,2.47968 1.72747,6.04736 2.36426,8.5636 0.67965,2.68555 0.65601,4.2146 2.36426,6.61731 1.90659,2.68176 4.64372,4.2987 6.30469,7.22931 1.09558,1.93304 3.34559,4.03503 4.72851,5.22687 1.50341,1.29565 3.57658,2.36535 4.72853,3.50329 1.01462,1.00226 4.88522,1.94318 6.30468,2.12891 2.00364,0.26214 3.77311,0.82708 5.71724,1.37439 2.13411,0.60077 4.64548,0.20483 6.4981,-1.16779 1.02698,-0.76086 -0.77832,-4.24164 -1.00179,-4.67102 -0.99316,-1.90839 -1.54182,-4.33331 -2.15057,-6.39465 -0.7133,-2.41541 -3.2378,-4.63117 -4.7285,-6.84003 -1.71132,-2.53577 -2.85521,-4.80493 -4.11353,-7.39587 -1.15574,-2.3797 -3.4768,-4.65931 -5.17299,-6.61731 -0.21011,-0.24256 -0.37638,-0.51905 -0.56456,-0.77851 -1.51307,-2.08648 -4.2671,-6.05395 -6.30468,-7.39587 -1.91443,-1.26074 -2.57248,-3.38574 -4.72853,-4.50763 -1.51815,-0.78998 -3.94144,-2.1109 -4.89114,-3.27747 -1.43098,-1.75769 -5.49389,-2.2351 -7.71824,-3.11401 z"
- id="path1640"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21516);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 114.03053,578.89795 c 0,0.55463 0,3.34888 0,5.0603 0,1.98175 -0.60463,3.71564 0,5.83887 0.60238,2.11523 0.5301,4.4187 0.78809,6.61731 0.24956,2.12677 1.66007,3.96155 1.97022,6.22809 0.24424,1.78497 0.76722,3.42083 1.18212,5.0603 0.43419,1.71564 2.44692,3.92511 3.77782,5.44958 1.55074,1.77625 1.59402,2.88892 3.70901,4.2818 1.42479,0.9383 2.17863,2.99176 3.54637,3.89252 1.18422,0.77991 2.11203,2.03967 3.15235,2.72479 1.67752,1.10474 2.61474,1.60889 3.94043,2.72479 0.96545,0.81269 2.02943,0.86628 2.75831,1.94629 0.40143,0.59485 3.49369,0.57184 4.55542,0.57184 1.37957,0 2.41646,1.06043 3.55643,1.37445 1.46507,0.40357 2.95735,0.22632 4.49751,0.38922 0.79442,0.0841 1.6995,0.0485 2.36426,-0.38922 0.35787,-0.23572 -2.91732,-2.88189 -3.15234,-3.11407 -1.24377,-1.22864 -3.91245,-2.31977 -5.32599,-3.11402 -1.76857,-0.99371 -3.98071,-2.13067 -6.10126,-2.72479 -2.05922,-0.57696 -4.47308,-3.57843 -6.21232,-4.83539 -2.36428,-1.94629 -3.1861,-3.46893 -3.63875,-5.47162 -0.46654,-2.06414 -2.61295,-3.88604 -3.05998,-5.65246 -0.44732,-1.76752 -2.53217,-4.34528 -3.63875,-6.22809 -1.39268,-2.36957 -2.07179,-5.21081 -3.54638,-7.39581 -1.36393,-2.02106 -2.09697,-5.33014 -2.7583,-7.55182 -0.65591,-2.20343 -1.44467,-3.41181 -2.36427,-5.68286 z"
- id="path2424"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21518);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 117.62649,613.33502 c -0.95896,0.71271 -1.51609,1.89642 -1.78922,3.89252 -0.25249,1.84521 0.10202,3.75207 0.60709,5.44958 0.53379,1.79407 0.39404,4.10089 0.39404,6.02143 0,1.98449 0.61543,4.00335 0.61543,6.04547 0,2.22638 0.17266,4.37036 0.17266,6.61731 0,2.18127 -0.97097,3.53619 -1.97021,5.22271 -1.02344,1.7273 -2.56709,2.86084 -3.94043,3.51319 -1.5805,0.75073 -2.76686,1.34924 -4.33447,1.59692 -1.39193,0.21985 2.77092,0.0428 3.15234,0 2.36523,-0.26574 4.00322,1.70349 6.30468,1.91632 1.68061,0.1554 3.4785,0.008 5.12256,-0.18219 2.09306,-0.24194 4.06287,-1.65912 5.91065,-1.94629 2.11486,-0.32873 2.7583,-0.93475 2.7583,-3.11407 0,-2.04559 0.62059,-3.42572 -1.57617,-3.70019 -0.0565,-0.007 -2.68935,-3.59345 -2.7583,-3.69562 -0.91829,-1.36072 -1.9531,-3.86316 -2.7583,-5.24713 -1.05182,-1.80786 -1.48302,-3.33765 -1.97021,-5.26276 -0.46454,-1.83557 -0.78809,-3.44232 -0.78809,-5.44958 0,-1.88501 0.24862,-2.80762 0.55473,-4.2818 0.42027,-2.02393 0.29126,-3.14478 -0.94877,-4.67109 -1.10332,-1.35791 -1.98165,-1.1903 -2.75831,-2.72473 z"
- id="path2439"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient21520);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 114.65139,575.42218 c -0.81721,6.51343 -0.016,13.08185 0.90656,19.54053 2.13143,11.46295 8.49806,21.31042 17.81868,28.37317 6.85489,4.62408 13.6652,5.91815 21.8658,6.34783 0.25252,0.0322 0.50505,0.0645 0.75757,0.0967 l -1.99446,3.12342 c -0.23981,-0.0368 -0.47963,-0.0736 -0.71945,-0.11041 -7.91312,-0.76905 -15.51169,-3.52735 -21.9765,-8.16944 -9.13541,-6.94836 -16.04407,-17.04639 -18.41729,-28.26782 -0.94058,-6.35907 -1.42607,-12.74744 -1.78722,-19.14899 l 3.54631,-1.78497 z"
- id="path6427"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 154.29961,631.04584 c -7.74601,-4.04181 -14.55019,-7.89276 -20.72956,-14.20752 -5.68478,-5.70771 -10.32462,-12.40534 -13.62089,-19.65033 -2.36704,-5.0152 -3.75131,-10.35102 -5.09623,-15.69501 -0.32206,-1.07965 -0.51108,-2.18054 -0.65468,-3.2934 l 1.41194,-1.20709 c 0.0871,1.13306 0.1825,2.26636 0.41893,3.38153 1.19975,5.3783 2.75927,10.68 4.8239,15.80115 3.14527,7.27618 7.64082,13.90704 13.11109,19.72491 6.12135,6.51142 13.7889,11.219 22.45339,13.62568 l -2.11789,1.52008 z"
- id="path1465"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 153.95935,630.70721 c -2.84987,-16.06787 -14.87323,-36.11059 -28.01517,-46.66992 -8.15017,-5.19562 -4.03821,-2.77167 -12.32353,-7.29223 l 0.56293,-0.36213 c 8.18698,4.61896 4.08562,2.18341 12.29371,7.32355 13.12095,10.20276 25.36204,30.03082 29.61028,45.47327 l -2.12822,1.52746 z"
- id="path1515"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 119.14735,591.92596 c 4.40916,0.396 8.78158,0.74457 13.21848,0.59339 4.50828,-0.14691 6.73639,-0.81751 10.98855,-2.53967 l 0.59989,0.73022 c -4.00083,1.27844 -8.0403,2.56604 -12.19277,2.66895 -4.8613,0.25128 -8.53306,-0.0535 -13.39746,0 l 0.78331,-1.45289 z"
- id="path1533"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 143.42114,604.38214 c -2.81289,-3.21442 -2.5812,-9.00622 -1.94185,-13.05096 l 1.31945,-0.63391 c -0.83982,3.78949 -1.34783,9.01379 1.01645,12.69964 l -0.39405,0.98523 z"
- id="path1555"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 141.52585,623.06634 c -9.78934,-4.67102 -6.76247,-16.16424 -4.41255,-22.85083 1.1647,-3.23223 2.95433,-6.28137 4.51944,-9.39453 l 2.18244,-0.79315 c -2.01472,3.17597 -4.09921,6.336 -5.49076,9.7309 -2.29656,6.00738 -5.60898,17.46881 2.71196,22.32788 l 0.48947,0.97974 z"
- id="path1561"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 125.70233,617.08185 c -0.10631,4.61383 -4.75876,7.26623 -8.69462,4.63837 -5.17423,-3.18519 0.0718,-12.12305 5.13456,-9.21296 0.4695,0.26618 0.65411,0.77948 0.97438,1.16114 l -1.69377,0.94354 c -0.25942,-0.3775 -0.29001,-0.92444 -0.79315,-1.15424 -4.00458,-2.17389 -5.96758,3.79712 -2.35725,7.46063 3.24655,2.01056 5.52023,2.01197 5.59961,-2.80706 l 1.83024,-1.02942 z"
- id="path1593"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 118.94865,618.34009 c 1.12739,-1.42011 2.46082,-2.65027 0.32815,-3.69904 -2.33658,-0.49469 -2.48548,0.45044 -2.50176,4.25086 0.70617,5.88781 1.38251,11.78741 1.54449,17.71362 0.0572,4.29449 -0.12108,8.67407 -3.5698,12.07159 -2.43104,2.03815 -2.20301,2.45789 -6.07017,3.79181 -0.75832,0.12836 -1.51634,0.24573 -2.28014,0.34851 l 1.57538,-0.97339 c 0.73726,-0.10076 1.47179,-0.38073 2.20766,-0.48913 2.36426,-1.77692 -0.79735,0.53418 2.93801,-2.11176 3.62652,-3.37707 4.13351,-7.7796 4.09677,-12.17218 0.11097,-5.93615 -0.9585,-11.72473 -1.85316,-17.58282 -0.0606,-3.08338 0.75223,-6.32764 5.49882,-5.72834 2.15923,1.08954 1.19139,2.23755 -0.0138,3.77845 l -1.90042,0.80182 z"
- id="path1609"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 130.31332,653.29462 c -0.3735,-1.78412 0.0586,-3.75037 0.27555,-5.57306 0.39798,-2.40021 -1.26255,-4.33325 -2.65371,-6.30683 -2.06914,-2.63934 -3.41127,-5.57281 -4.42425,-8.59265 -1.31361,-4.1734 -1.45717,-8.53442 -1.44562,-12.82745 0.0468,-0.64825 0.091,-1.29669 0.13391,-1.94513 l 1.68082,-0.69464 c -0.0948,0.66516 -0.19674,1.32983 -0.27109,1.99689 -0.24117,4.30627 -0.25522,8.68469 1.27737,12.85009 1.03189,2.99884 2.58234,5.8368 4.59846,8.47004 1.38884,1.99054 3.20846,3.96197 2.73268,6.37811 -0.20826,1.69403 -0.59081,3.58789 -0.17858,5.20654 l -1.72554,1.03809 z"
- id="path1613"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 132.4883,651.87122 c -2.78771,1.87909 -6.68902,1.72131 -10.24142,1.82556 -3.29944,0.13806 -6.44543,-0.82269 -9.70376,-1.16309 -2.56189,-0.35718 -3.45444,-0.38824 -5.90705,0.15839 l 1.18213,-0.77857 c 2.4592,-0.52465 3.68017,-0.67218 6.23877,-0.2655 3.18691,0.4314 6.29336,1.34106 9.54846,1.2265 2.05551,-0.0597 10.07308,-1.1062 6.98245,-0.20141 l 1.90042,-0.80188 z"
- id="path1617"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer17"
- display="none"
- style="display:none">
- <g
- id="g37608"
- transform="matrix(0.699355,0,0,0.699355,-784.3132,-487.8763)">
- <path
- d="m 564,728.09448 c 2.33795,7.77741 -0.0848,15.97809 -3.42859,23 -3.29108,6.91126 -10.48749,8.56439 -17.57141,10.42859 -9.69281,2.55072 -19.75464,0.59741 -29,5 -9.89358,4.71124 -24.86206,3.57141 -35.57144,3.57141 -10.90189,0 -24.55487,-0.0754 -35,-3 -8.64646,-2.42102 -15.48014,-1.02582 -23.42856,-5 -7.44629,-3.72314 -10.00421,-8.77209 -11.57144,-17 -1.34656,-7.06958 0,-14.35254 0,-21.42859 0,-6.26751 2.93374,-9.47961 7.57144,-12.57141 6.83804,-4.55871 25.8598,-4.09436 34.57144,-6 14.22202,-3.11108 27.77133,-4 42.42856,-4 10.98901,0 22.17572,2.19599 33,4 9.7088,1.61817 18.70209,3.46802 27,9 7.0202,4.68018 14.48523,6.93799 15.42859,17 0.78235,8.34485 -0.60944,16.8039 -5.42859,23"
- id="path35279"
- inkscape:connector-curvature="0"
- style="fill:#4190c9;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 405.57144,726.52307 c 0,7.55701 2.85712,14.362 2.85712,22.14282 0,7.09613 -1.34253,10.60193 4.57144,14.42859 4.41394,2.85608 12.41473,2 17.57144,2 10.14612,0 21.00913,5.20581 30.42856,7 12.66507,2.41242 27.98093,2 41,2 2.14285,0 4.28571,0 6.42856,0 5.08353,0 9.83426,-0.75427 13.57144,-2 10.89624,-3.63208 22.44385,-0.71069 31.42859,-7 2.84637,-1.99249 6.62353,-10.06525 8.57141,-13.57141 3.28149,-5.90668 4.30054,-10.60083 3,-17.42859 -0.85034,-4.46423 -1.53937,-7.84643 -5,-9 -6.86127,-2.28711 -15.07947,4.31409 -21,6.42859 -6.18066,2.20734 -16.5495,2.57141 -23.42859,2.57141 -4.35107,0 -9.78229,1 -14.99997,1 -8.66424,0 -17.05749,1 -25.57144,1 -8.86056,0 -16.40851,-2.42859 -25,-2.42859 -8.26688,0 -16.57873,-4.01874 -24.42856,-5 -4.93213,-0.61651 -19.31592,-2.56091 -22.57144,-0.57141"
- id="path36041"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient37622);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 471.271,718.67993 c -6.465,0.84198 -12.64661,3.34107 -18.68637,5.74512 -6.51624,2.29529 -12.11448,7.77521 -16.47346,1.61493 -0.39453,-1.19245 -0.50915,-2.44837 -0.7633,-3.67035 l 10.25116,-5.44165 c 0.24756,1.15198 0.3515,2.34039 0.74371,3.46075 4.23197,1.09278 9.26102,0.60431 15.11096,-2.9469 6.3898,-2.44708 12.8862,-4.97796 19.68711,-5.9328 l -9.86982,7.1709 z"
- id="path36817"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 487.01471,710.08051 c 3.40561,1.8092 6.85309,4.096 10.15869,6.26776 4.56406,2.28326 8.43661,5.81775 10.97626,9.02337 -10.01609,7.71057 -15.42536,7.63611 -26.68399,7.52301 l 9.17657,-6.65082 c 8.22128,0.07 25.18088,-4.61181 6.34894,3.61408 -0.43304,-0.38721 -6.50095,-6.11768 -9.52972,-7.31146 -3.0719,-1.89844 -7.3436,-4.83435 -10.31656,-5.29511 l 9.86981,-7.17084 z"
- id="path36819"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 495.58615,707.22333 c 4.659,2.34833 10.50021,2.20276 15.72083,2.5047 8.05117,0.25482 16.07126,0.31866 24.07498,1.23242 16.0426,1.90344 -2.42743,9.76099 -10.15601,12.75098 -1.03522,0.45495 -2.09692,0.84405 -3.14538,1.26605 l 8.93664,-7.02625 c 0.005,-0.002 3.19837,-1.29034 3.2052,-1.30725 11.53858,-4.73639 -1.89837,5.85236 -8.07916,0.83661 -7.99457,-0.80542 -16.02216,-0.69397 -24.04886,-1.13397 -5.2532,-0.33429 -11.94675,-0.68347 -16.37805,-1.95245 l 9.86981,-7.17084 z"
- id="path36821"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 475.5567,714.39417 c -4.50107,-1.58918 -8.82037,-4.22913 -13.24341,-6.25531 -9.81796,0.33416 -11.89083,3.76098 -3.03021,-1.12146 l -10.57199,4.15472 c 7.84457,-4.72241 14.19647,-11.22821 23.17355,-9.4148 4.38025,2.0221 8.74042,5.07099 13.5419,5.46601 l -9.86984,7.17083 z"
- id="path36823"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 448.4433,724.36621 c 3.73669,0.33521 7.4545,0.82721 11.20682,1.12354 l -9.44895,6.77667 c -3.87207,-0.29999 -7.74435,-0.92005 -11.62771,-0.72937 l 9.86984,-7.17084 z"
- id="path36825"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 507.01471,725.7948 c 2.3728,-1.11578 5.25812,-2.13415 7.87707,-3.06226 l -9.22149,7.16748 c -2.78375,1.09351 -5.64716,2.51679 -8.52539,3.06562 l 9.86981,-7.17084 z"
- id="path36827"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 532.69958,718.67993 c -3.70013,-1.66266 -7.04217,-4.43744 -10.59796,-6.57001 l 9.63275,-6.22302 c 3.45057,2.03174 6.90027,5.09528 10.83503,5.62213 l -9.86982,7.1709 z"
- id="path36829"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 472.729,698.65192 c 2.09427,0.15838 4.19831,0.12225 6.29712,0.14929 l -9.45889,6.87225 c -2.23599,0.0278 -4.47577,-7.9e-4 -6.70804,0.14929 l 9.86981,-7.17083 z"
- id="path36833"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 558,756.09448 c 1.28107,-5.80218 -0.17188,-11.53131 0.21674,-17.44031 0.96649,-6.62097 0.62146,-13.22021 -0.7085,-19.71698 -2.78125,-5.00415 -8.12207,-6.27533 -13.21228,-8.17395 -6.8371,-2.62457 -13.91278,-4.59942 -21.01282,-6.37725 -9.06958,-2.0564 -18.29257,-3.09534 -27.55078,-3.78223 -8.93957,-1.0069 -17.67859,-0.10657 -26.50458,1.26764 -7.72433,0.98602 -15.37375,2.34619 -23.13519,3.01971 -8.28339,-0.25635 -15.98526,2.62097 -23.87857,4.69208 -24.06818,10.90698 -2.21011,-6.39948 -10.77637,6.64642 -1.80267,5.72528 -0.62594,11.95929 -0.36212,17.90088 0.1185,6.68689 -0.14813,13.34125 1.28458,19.82355 3.71569,6.7038 9.64862,8.31195 16.74026,9.39337 7.89102,0.35614 14.16696,2.16974 22.07257,2.06958 7.22574,0.095 15.69153,2.70453 22.82706,1.67749 10.51523,1.4621 16.59991,1 27.26562,0.43494 8.00101,0.99231 18.14283,0.23413 26.17591,0.56506 11.0708,0.19068 19.83355,-3.25189 30.60949,-6.02905 -12.0072,8.32233 -2.72589,3.66028 -0.062,-5.78168 0.48285,-1.88354 0.47309,-3.82599 0.65522,-5.7464 L 567,743.09448 c -0.23615,1.96906 1.74255,6.16785 1.43139,8.12854 -2.77356,11.28381 -7.81793,12.81275 -19.43139,17.87146 -10.86615,2.03485 -19.4126,5.43445 -30.43152,5.42145 -8.17224,0.0153 -15.06342,0.57855 -26.06342,0.57855 -11,1 -17.02502,-1 -27.75897,-1.09241 -7.10876,0.9928 -15.55572,-1.76025 -22.74609,-1.90759 -7.97391,0.0647 -14.32666,-1.55762 -22.27582,-2.12646 -7.44913,-1.27509 -13.51034,-3.44184 -17.52063,-10.32843 -1.42697,-6.62708 -1.2439,-13.39527 -1.38507,-20.23615 -0.27182,-6.12366 -1.51138,-12.5069 0.1427,-18.43426 6.04819,-10.47174 18.9389,-15.16407 30.19739,-18.21772 7.98593,-2.14105 15.94635,-5.43542 24.33636,-5.19799 7.64432,-0.71332 15.05612,-1.5022 22.68152,-2.38971 8.91174,-1.35474 17.71542,-2.23639 26.73041,-1.13703 9.32111,0.70508 18.54535,2.07636 27.71582,3.90265 7.17316,1.74176 14.29797,3.76721 21.20111,6.39557 5.51141,1.91351 11.17078,3.44153 13.89844,9.01856 1.31982,6.66027 1.72961,13.44043 0.73517,20.22058 -0.44471,5.73803 -0.59327,11.53198 -1.02234,17.25274 L 558,756.09448 z"
- id="path35258"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 571.64606,720.08893 c -10.01667,5.1676 -24.82434,9.72894 -35.86664,11.68207 -13.75818,1.8753 -27.58527,3.29425 -41.43057,4.35864 -14.96918,1.19885 -30.00983,0.79877 -44.96158,-0.40582 -14.66086,-1.10633 -28.84162,-4.46772 -42.89395,-8.60456 -11.80274,-3.26312 -12.94837,-3.19183 -14.88758,-4.02313 l 6.22907,-4.42816 c 1.89398,0.84674 2.55444,1.16882 14.30417,4.31427 14.05096,4.20733 28.32577,7.28595 42.98855,8.3623 14.92887,1.37207 29.92405,1.7674 44.87247,0.3205 13.83899,-1.05737 27.67096,-2.40637 41.40363,-4.42658 11.09558,-2.09845 26.01208,-6.82843 36.03381,-12.14398 -10.46558,6.79914 0.76489,1.0102 -5.79138,4.99445 z"
- id="path35276"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer1-0"
- display="none"
- style="display:none">
- <g
- id="g11326"
- transform="matrix(0.748816,0,0,0.748816,-748.2099,-403.0788)">
- <path
- d="m 705.26691,167.24388 c -1.90894,8.99153 -4.28876,12.57513 -16.16803,12.57513 -15.71698,0 -32.33008,1.79645 -48.50409,1.79645 -21.23749,0 -43.73108,-1.3759 -64.67212,1.79645 -1.8064,0.27365 -3.5929,0.66551 -5.38934,0.99828 -6.39234,1.18403 -9.70551,4.39106 -17.02497,4.39106 -6.00207,0 -10.8302,2.86767 -17.10754,2.86767 -4.15125,0 -7.18579,-21.34119 -7.18579,-24.42504 0,-8.5824 -1.79645,-19.30339 -1.79645,-28.74316 0,-11.34375 1.79645,-21.70164 1.79645,-32.33607 0,-10.092978 -5.38935,-17.698661 -5.38935,-27.913642 0,-9.28952 0,-18.296227 0,-25.979797 0,-13.970242 16.95185,-12.57513 26.05335,-12.57513 12.974,0 25.45074,0 38.61877,0 12.96192,0 27.17365,1.062401 38.50537,-3.592899 6.21241,-2.552181 40.08039,-7.185789 45.92768,-7.185789 10.20715,0 17.22961,-5.062592 21.55737,3.59289 3.51721,7.034409 1.79645,25.984528 1.79645,34.132511 0,10.778687 0,21.557381 0,32.336067 0,10.778679 0,21.557369 0,32.336069 0,11.86368 3.54254,22.97801 10.77869,32.33606 0.81909,1.05925 -1.19764,2.39528 -1.79645,3.59289 -5.13959,10.27922 9.7055,4.73406 0,7.18579"
- id="path1645"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient10558);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 694.48822,189.85422 c -4.00678,-2.00339 5.80328,11.31521 1.79645,13.31862 -9.31989,4.65997 -17.65558,-6.44199 -26.94672,-1.79645 -7.86993,3.935 -12.99719,-3.5929 -22.37323,-3.5929 -8.13202,0 -10.87952,-4.54525 -12.70282,-11.55864 -1.60376,-6.16908 -0.62933,-17.54078 0.94354,-23.59091 1.97736,-7.60585 -1.44427,-9.76384 -5.38934,-15.15098 -4.56006,-6.22695 -12.3761,-6.78779 -16.16803,-14.37159 -2.23022,-4.46051 -12.43091,-8.83802 -16.16803,-12.57514 -5.14624,-5.14623 -14.36926,-10.77982 -7.18579,-14.37158 8.23407,-4.11703 15.57678,5.0937 19.76092,7.18579 6.44855,3.22428 8.87354,8.57849 16.948,12.57513 7.02747,3.47844 15.32532,6.45842 21.77625,8.98224 10.89703,4.26333 3.2713,-8.42841 2.59405,-10.77868 -2.86163,-9.93123 -1.11218,-19.85918 8.98224,-24.10188 8.41309,-3.536039 17.78449,-0.240171 23.35382,2.5445 4.50281,2.2514 -3.35412,22.87636 -3.59289,23.35382 -7.95282,15.90567 -0.52338,7.93542 5.38934,19.76092 2.73529,5.47055 1.04913,12.8769 3.5929,17.9645 3.17828,6.35658 -2.09772,10.1176 3.59289,14.37158 3.55982,2.66109 3.5929,18.57912 3.5929,23.35382"
- id="path2418"
- inkscape:connector-curvature="0"
- style="fill:#fdffbb;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 695.44824,183.1831 c 16.03907,-3.66796 4.18274,9.28157 7.34052,-4.4288 0.0823,-7.15416 -2.99164,-13.71545 -5.61982,-20.21824 -4.82672,-10.8768 -5.85406,-22.43361 -5.7857,-34.18346 0.84271,-22.21136 1.30255,-44.430732 1.4906,-66.657088 -0.58521,-9.371059 2.82452,-22.757309 -6.61523,-28.33934 -12.64655,-2.57959 -25.81855,1.93343 -38.09546,4.975418 -16.10956,3.797852 -32.5116,5.97168 -48.97925,7.442471 -19.45056,1.653042 -38.98773,2.42857 -58.47852,1.101852 -5.36896,-0.459061 -10.72235,-1.108822 -16.0545,-1.883362 l 5.24377,-3.685028 c 5.17316,0.844879 10.34656,1.736099 15.57672,2.159069 19.39551,1.546329 38.86902,0.563839 58.23053,-1.03849 16.63935,-1.560932 33.16602,-4.05648 49.43946,-7.903341 12.52777,-3.16552 25.94916,-7.47691 38.86706,-4.96093 9.93903,5.86549 5.74109,19.652281 6.12995,29.461531 -0.6319,22.240913 -2.0365,44.47364 -1.67951,66.730078 0.4671,11.44744 2.07422,22.85432 6.66626,33.4427 2.78595,6.53409 5.89527,13.11273 6.14118,20.33455 -1.4654,8.87646 -10.82032,12.50197 -19.17383,11.53119"
- id="path1635"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 671,206.64897 c -3.12518,-0.21808 -6.10968,-1.41714 -9.14398,-2.18837 -5.35871,-1.23176 -10.84784,-1.58613 -16.25086,-2.53844 -5.27478,-0.40945 -9.30719,-1.8001 -12.987,-5.48497 -2.24981,-4.0595 -1.60736,-8.66876 -1.06006,-13.1096 1.09009,-4.76738 2.39704,-9.46251 3.13599,-14.30128 0.65051,-4.43287 -0.11798,-8.40081 -2.16327,-12.30578 -3.28039,-4.28405 -7.31518,-7.89363 -11.27142,-11.53312 -4.45239,-3.88806 -9.23316,-7.36879 -13.66773,-11.27217 -5.09033,-4.91075 -10.08795,-9.91941 -15.13647,-14.87552 -4.10871,-4.2543 -8.13098,-8.49134 -13.01917,-11.8431 -8.43951,-3.07397 1.91816,-7.568524 7.01044,-7.391918 5.21649,0.740838 9.97034,2.909438 14.68335,5.137448 5.36035,3.11459 9.97913,7.3009 15.04529,10.86423 5.13061,3.50661 10.67279,6.31223 16.29333,8.92959 5.69733,2.61739 11.76563,4.18482 17.77039,5.89336 10.16022,1.56049 5.11572,1.48101 1.8949,2.55142 -0.63703,-3.36032 -3.19464,-5.80431 -5.18348,-8.47635 -3.10766,-4.14875 -3.36749,-8.2654 -2.67065,-13.20497 1.23627,-4.6485 4.79077,-7.81858 8.40228,-10.73409 6.89807,-4.939314 11.3772,-6.819044 19.21613,-5.199018 4.36816,1.277031 7.73785,3.814507 10.66791,7.150948 2.12304,3.5782 2.24664,7.35224 1.91485,11.364 -0.74414,4.0525 -2.62365,7.75947 -4.28589,11.49585 -1.79669,3.8883 -1.73736,7.1555 0.13239,10.85125 2.33081,2.31508 4.99444,3.91671 7.11017,6.44057 1.62988,3.29883 2.34619,6.92557 2.92566,10.53224 0.29412,4.10933 1.1947,8.12911 1.78521,12.1986 0.1211,4.26915 1.0083,8.36187 2.17737,12.44204 1.48383,3.55397 3.05933,7.06978 4.60181,10.59876 1.82721,3.43835 2.77038,6.94858 2.94189,10.68156 -4.66345,5.32807 -9.73523,6.32723 -16.15863,5.93907 -4.51812,-0.22233 -8.60461,-1.60856 -12.70068,-3.37547 -0.80072,-1.8753 5.60437,-5.81947 0.79376,-0.18019 -2.33203,1.31578 -4.66412,2.63155 -6.99622,3.9473 -0.22479,0.0748 -0.44964,0.14953 -0.67443,0.22429 l 5.83752,-4.57424 c 0.21765,-0.0935 0.43524,-0.18693 0.65289,-0.28041 -1.92529,1.12393 -3.85058,2.24783 -5.77581,3.37175 3.11865,-3.28548 7.24377,-8.45229 12.28308,-6.59952 4.00409,1.78334 8.05688,3.03449 12.48834,3.22853 10.47082,0.3212 3.40344,0.40615 3.44787,1.44364 0.2555,-3.5943 -0.78454,-6.83885 -2.54028,-10.21396 -1.56396,-3.55087 -3.17334,-7.08608 -4.62988,-10.68184 -1.18396,-4.12039 -2.11389,-8.26161 -2.23169,-12.57233 -0.5708,-4.06729 -1.49316,-8.07416 -1.81628,-12.17433 -0.56006,-3.49548 -1.24305,-7.0193 -2.81263,-10.22009 -2.22796,-2.33913 -4.89514,-4.05457 -7.11468,-6.43645 -1.9657,-3.8912 -2.13477,-7.38333 -0.38922,-11.53183 1.63324,-3.69724 3.51715,-7.33752 4.34033,-11.32312 0.41522,-3.83467 0.3576,-7.41563 -1.67181,-10.85888 -2.80426,-3.19105 -6.05304,-5.64144 -10.26423,-6.766798 -6.26007,-1.146774 -15.71966,2.583118 -6.46905,-1.601044 -3.60913,2.771582 -7.18628,5.770572 -8.63995,10.246462 -0.80866,4.74317 -0.59174,8.67035 2.42132,12.66712 2.12445,2.73355 4.75971,5.22366 5.53394,8.69486 -4.77014,4.38836 -7.02979,8.10914 -14.41028,4.96048 -6.06122,-1.61789 -12.04602,-3.49155 -17.81073,-5.99434 -5.67212,-2.59469 -11.1908,-5.52421 -16.35767,-9.03524 -5.02313,-3.57689 -9.66571,-7.71431 -15.03283,-10.76685 -4.71686,-2.17282 -9.50623,-4.23002 -14.72535,-4.81443 -7.31384,0.70313 2.42725,-5.656792 5.07557,-0.74983 4.84069,3.45471 8.85034,7.72432 12.86126,12.1023 5.08899,4.91877 10.05481,9.96749 15.25458,14.7692 4.47473,3.86322 9.25012,7.35857 13.68207,11.27465 4.0202,3.67041 8.16315,7.27899 11.48792,11.61633 2.21899,4.0164 3.0437,8.12795 2.42541,12.74506 -0.72552,4.87019 -2.03326,9.5869 -3.1933,14.36651 -0.53515,4.20912 -1.1438,8.63626 0.90436,12.51593 3.5318,3.51499 7.52685,4.6593 12.53552,5.1321 5.44123,0.88096 10.94886,1.35225 16.34436,2.51663 3.09833,0.82107 6.19739,2.0276 9.44483,1.76132 L 671,206.64897 z"
- id="path1637"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 628.13477,187.65897 c -4.35883,-2.46296 -9.11554,-3.41462 -14.01795,-4.13218 -8.08966,-0.79467 -16.22369,-1.10745 -24.34228,-0.76091 -8.29224,0.46721 -16.44025,2.1407 -24.6269,3.4397 -8.43286,1.1321 -16.79571,2.71079 -25.22003,3.88649 -8.24267,0.50685 -11.06823,-4.60406 -13.09588,-11.69864 -1.84894,-10.15773 -1.87091,-20.55015 -2.10388,-30.84491 -0.32117,-12.50766 -0.13013,-25.02028 -0.25629,-37.52879 -0.38141,-15.133668 -2.37665,-30.159768 -3.10327,-45.271134 -0.47949,-7.189526 -0.15082,-14.329025 1.44787,-21.342587 l 9.08533,-4.2938 c -2.07684,6.957481 -2.80151,14.143471 -2.33148,21.414921 0.77856,15.080658 2.91717,30.069473 3.31823,45.17648 0.23822,12.46638 0.27125,24.93724 0.50727,37.40454 0.25104,10.17582 0.3789,20.44724 2.37103,30.46196 2.01965,6.64196 4.63831,11.15389 12.37128,10.39823 8.35626,-1.17385 16.68573,-2.53577 25.03003,-3.79546 8.22821,-1.30723 16.4107,-3.05285 24.75952,-3.39238 8.16009,-0.26291 16.33777,0.0653 24.46472,0.87825 5.05646,0.6852 9.82062,2.13687 14.60797,3.55919 l -8.86529,6.44103 z"
- id="path3184"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 677.89441,172.73814 c -0.002,2.23812 0.73846,4.39977 1.43481,6.50545 1.11365,2.72992 2.04657,5.52255 2.52478,8.40192 -3.0885,3.1895 -4.75567,3.84903 -7.77337,1.40312 -1.49402,-3.94875 -0.43702,-8.35167 0.10247,-12.44013 0.0322,-0.30314 0.0645,-0.60629 0.0967,-0.90947 l 3.00531,-1.48734 c -0.0397,0.31069 -0.0795,0.62141 -0.11926,0.93212 -0.60455,3.96485 -1.53454,8.30358 -0.0684,12.12975 3.172,2.32819 3.45807,1.22118 1.59033,1.92786 -0.30792,-2.83352 -1.27179,-5.60334 -2.34381,-8.30306 -0.73596,-2.11597 -1.43725,-4.27147 -1.70416,-6.50192 l 3.25458,-1.6583 z"
- id="path3190"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 550.54004,77.421478 c -1.72986,7.348213 -4.09552,15.451775 -1.02057,22.631582 4.66394,8.21469 18.56134,0.22686 9.25317,5.61225 -0.75146,0.43483 1.39063,-1.06371 2.08594,-1.59559 l 6.72394,-2.888 c -8.13342,5.40147 -16.58301,12.74085 -24.24115,2.69956 -3.07959,-7.251077 -0.633,-15.060693 0.27124,-22.669534 l 6.92743,-3.790268 z"
- id="path3208"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.80000001;stroke-miterlimit:4" />
- <path
- d="m 545.70654,80.567383 c 5.35315,0.11467 10.68341,-0.868446 15.98596,-1.581444 1.43793,-0.16082 2.88068,-0.262497 4.3219,-0.381477 l -5.74633,4.555222 c -1.44751,0.133194 -2.89807,0.234718 -4.33973,0.434967 -5.51934,0.695229 -11.03674,1.425186 -16.57458,1.929031 l 6.35278,-4.956299 z"
- id="path3212"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.80000001;stroke-miterlimit:4" />
- <path
- d="m 545.70654,91.428703 c 4.76868,0.09528 9.5553,-0.653091 14.30542,-1.15065 l -5.76355,4.598709 c -4.97131,0.448204 -9.91687,1.157196 -14.89465,1.508224 l 6.35278,-4.956284 z"
- id="path3214"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.80000001;stroke-miterlimit:4" />
- <path
- d="m 575.00317,86.499458 c 4.28626,-0.04827 8.53382,-0.967537 12.7497,-1.751579 l -5.66248,4.573349 c -4.47369,0.747482 -8.94018,1.598511 -13.44,2.134521 l 6.35278,-4.956291 z"
- id="path3216"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.80000001;stroke-miterlimit:4" />
- <path
- d="m 575.92267,95.385963 c 3.37372,0.402519 6.73913,-0.261902 10.08362,-0.692894 l -5.80176,4.634552 c -3.53803,0.43792 -7.07147,0.903399 -10.63465,1.014629 l 6.35279,-4.956287 z"
- id="path3218"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.80000001;stroke-miterlimit:4" />
- <path
- d="m 597.47003,84.975227 c 1.80853,-8.206551 12.42188,-15.728874 21.10913,-13.624916 4.24811,8.190758 -6.09106,14.439659 -5.98663,23.142586 0.0888,0.456932 0.17749,0.913857 0.26624,1.370796 l -7.00342,3.753883 c -0.10492,-0.460678 -0.20984,-0.921349 -0.31476,-1.382019 -0.44733,-8.852806 9.56323,-14.395988 6.38361,-22.555481 -8.76264,-1.260117 3.51751,-6.636536 -7.4878,4.23381 l -6.96637,5.06134 z"
- id="path10564"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 608.75592,108.96693 c -6.38025,-5.62193 -0.5191,-7.86353 5.80188,-11.174281 1.79596,0.361618 0.72546,2.374901 1.00861,3.301831 l -7.53888,3.63746 c -0.0647,-0.51591 0.90136,-1.94972 -0.24103,-1.9221 12.58227,-7.299593 1.62738,-2.88463 7.93579,1.09576 l -6.96637,5.06133 z"
- id="path10566"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer5"
- display="none"
- style="display:none">
- <g
- id="g10519"
- transform="matrix(0.748816,0,0,0.748816,-672.2623,-344.7802)">
- <path
- d="m 828.46216,-24.905519 c -4.09387,0 -7.20831,-7 -13.46216,-7 -6.66669,0 -13.33331,0 -20,0 -4.82324,0 -13.79193,-3.05271 -17.55054,-6.000002 -3.05505,-2.395649 -7.79693,-4.168209 -11.44946,-7 -6.80762,-5.277889 -10.52118,-6.23941 -17,-3 -3.87176,1.93589 -8.32159,2.924973 -13,4 -5.05395,1.161331 -8.67737,6 -14,6 -4.69025,0 -10.52179,2 -16,2 -4.8045,0 -9.60901,0 -14.41351,0 -3.41791,0 -4.67597,7.578102 -5.05072,10.000002 -0.89112,5.75807 1.46423,12.46141 1.46423,18.410999 0,5.44529 1,10.66683 1,16.16244 0,6.69152 1.09058,12.986751 2,19.42656 0.86395,6.117861 0,10.96908 0,17.44401 0,4.460659 -1.43378,8.256401 -1.43378,12.555988 0,5.34903 0.4693,15.517773 5.43378,18.000004 5.79211,2.896057 11.9621,1.974747 18,6 3.98023,2.653481 9.86493,3 15.45709,3 5.32172,0 9.88965,2.193535 14.14209,5 5.64551,3.725838 10.35211,4.436409 17.40082,4.436409 5.09876,0 9.72315,4.818871 13,7.563589 3.38757,2.83748 7.63922,5.17081 11.52057,6 3.15485,0.674 9.05548,-1.04431 12.47943,-2 2.44513,-0.68248 1.86469,-4.72941 3,-6.999998 1.57135,-3.142746 7.99323,-6.496613 11,-8 5.35779,-2.678894 10.73804,-5.959244 16,-8 4.29193,-1.664566 8.80225,-2.753563 10,-8 1.24835,-5.467972 3,-8.416245 3,-14.403702 0,-4.962971 5.68231,-6.278599 9,-9.596302 3.48914,-3.489159 1.49622,-9.48864 3,-14 1.27637,-3.82909 -2,-9.317848 -2,-13.999998 0,-4.859991 2,-9.893531 2,-15.000001 0,-4.89448 -2.44995,-8.79997 -6,-11.54839 -4.32849,-3.35109 -6.24042,-5.96979 -7.43628,-11.45161 -0.88855,-4.07301 -4.51672,-7.471309 -7.0711,-9.999999 -1.52979,-1.5144 -3.9986,-2.752981 -6.49262,-4"
- id="path2016"
- inkscape:connector-curvature="0"
- style="fill:#b39473;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 836.5434,-19.905519 c -2.91358,-2.8843 -9.67755,1 -14.5434,1 -5.36304,0 -9.5224,6.999999 -16,6.999999 -5.00543,0 -13.32629,0.442101 -18,2 -7.41833,2.47279 -9.5404,5.101571 -9.5404,13 0,7.43709 4.04064,14.020461 4.04064,21 0,10.802668 3.43287,19.507929 4.49976,29.999998 0.97015,9.540791 1.07117,17.001598 3,26.000004 1.51514,7.068626 0,14.355278 0,21.507478 0,6.49559 4.17859,5.83826 9,4.49252 1.04053,-0.29042 5.82953,-8.867567 8,-10.553442 4.44904,-3.45565 7.81824,-6.855675 13,-9.446556 7.28095,-3.64048 11.93005,0.139847 16,-8 3.8548,-7.709579 4.94666,-11.192612 3,-20.000004 -2.01623,-9.12225 -1,-20.69733 -1,-29.999998 0,-10.070261 3,-19.68836 3,-30.000001 0,-12.40149 -0.45087,-16.678159 -13.54797,-20.999999"
- id="path2776"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient10539);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 786,109.09448 c -4.04767,-0.80763 -9.23407,-4.69853 -13,-8.50268 -3.10388,-3.135403 -13.81677,-5.88879 -18,-6.497318 -6.90582,-1.004585 -13.0058,-3 -20.48199,-3 -5.18183,0 -10.83869,-5.660339 -15.51801,-8 -5.80194,-2.90097 -13.02771,-4.055359 -16,-10 -3.30066,-6.601341 -9,-10.950794 -9,-19.000004 0,-9.033607 -1,-18.595318 -1,-26.999998 0,-6.49374 -2.42364,-13.383491 -2.42364,-20.44686 0,-8.3269 -1.01014,-16.32334 -1.01014,-24.55314 0,-7.81382 2.09192,-8.88604 8.43378,-11 4.74597,-1.581991 11.32989,2.164959 15,4 4.4411,2.220549 9.01001,2.06855 14,4 7.7544,3.001459 16.33093,0.16547 24,4 6.3977,3.19887 16.33673,-2.32649 20,4.999999 2.75037,5.500731 6.37018,7.96779 8,14 1.61707,5.985 0.50781,12.92325 1.47992,19 1.28003,8.001179 4.04065,15.207949 4.04065,23.433859 0,6.438629 2.02026,11.333961 2.02026,17.566139 0,7.028713 0.45917,14.034031 0.45917,21.000004 0,5.845604 0.25159,11.188667 2,16.487167 0.5545,1.680362 0.62219,3.270282 1,5.050762"
- id="path3544"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient10541);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 830,-27.905519 c 0.63074,7.60409 -4.20178,8 -11,8 -7.57941,0 -14.0581,4.999999 -21,4.999999 -4.26953,0 -7.33368,4 -12.4693,4 -4.61151,0 -8.22272,1 -12.5307,1 -3.4693,0 -13.13861,-8.712859 -17,-9.999999 -1,-0.33333 -2,-0.66666 -3,-1 -4.46668,-1.488901 -10.66864,-1 -16.46173,-1 -6.66699,0 -12.06445,-0.2631 -17.53827,-3 -4.36829,-2.184151 -10.64447,-5.214821 -16,-7 -5.43903,-1.813002 -14.65692,0.138649 -17.47443,-1.000002 -3.78747,-1.53067 10.42048,-5 20.47443,-5 6.91937,0 24.79449,-5.183899 30.53827,-7.469719 5.05591,-2.01207 10.21796,-3.530281 16.46173,-3.530281 8.90173,0 13.43744,8.454193 21,10 11.58643,2.36832 21.43127,7.000002 34,7.000002 6.22809,0 10.5191,3 17.4317,3 4.96161,0 4.3244,-0.01089 5.05078,3"
- id="path4312"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient10543);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 746,79.094482 c 5.60284,-0.224564 11.70233,1.595367 14,-3 1.54706,-3.094063 2,-12.980694 2,-16.413864 0,-7.077438 1,-15.117809 1,-22.58614 0,-5.677439 -2.33984,-10.019489 -4,-14.999998 C 757.16547,16.59082 758.71521,11.95209 753,9.094479 748.6438,6.916379 746.23059,4.55175 741.58905,2.60701 736.92084,0.65109 732.59436,-2.05098 728,-3.90552 c -6.00995,-2.42594 -11.49176,-3.589 -18,-3.589 -4.68738,0 -7.39966,6.38834 -9,9.589 -1.69165,3.38333 -1,8.98092 -1,13 0,5.58327 0,10.988191 0,16.999999 0,8.886482 0.35638,15.17411 4,22.535381 2.87427,5.806862 10.34863,10.638931 16,13.464622 7.36615,3.683067 14.46533,5.732666 21,9 2.81708,1.408546 5.33325,2.22216 8,4"
- id="path5080"
- inkscape:connector-curvature="0"
- style="fill:url(#radialGradient10545);fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 736,6.09448 c 8.15662,2.65069 17.17816,7.81443 18.59717,17.086331 2.31488,3.098398 0.93744,14.57287 -3.52686,8.355309 -2.96472,-6.18063 -5.74438,-12.633129 -11.04089,-17.237029 -2.10205,-2.2404 -4.16138,-4.96564 -4.02942,-8.20461 z"
- id="path8165"
- inkscape:connector-curvature="0"
- style="fill:#ffffff;fill-opacity:0.70588199;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 772,59.094479 c 3.38727,0.541561 6.69513,-0.30064 8.47992,5.000004 1.30774,3.88372 -0.0215,7.270851 -1.01013,11 -0.99585,3.756355 0.38787,8.348557 -0.46979,12 -0.75067,3.195969 -7.5318,-1.063644 -9,-4 -1.73248,-3.464981 1,-9.147575 1,-14 0,-6.008232 -0.80853,-12.037354 7,-9.000004"
- id="path9719"
- inkscape:connector-curvature="0"
- style="fill:#8f7253;fill-opacity:0.80542997;fill-rule:evenodd;stroke-width:1px" />
- <path
- d="m 689.91241,-35.315071 c -3.30383,5.658741 -1.6864,11.999161 -0.54736,18.104851 0.78613,7.380711 1.92206,14.71198 3.14154,22.03152 1.08686,6.86599 2.40216,13.65486 2.88245,20.595021 0.51233,6.253119 -0.25458,12.47221 -0.56592,18.710278 -0.37225,6.033562 -1.09845,12.005421 -0.88721,18.0546 0.49823,4.74852 2.46375,8.480812 5.54804,11.955521 3.88153,3.010551 8.65301,4.16526 13.32379,5.363922 6.48383,2.226822 13.00482,4.373886 19.49194,6.604881 5.34919,2.293526 11.05603,2.741821 16.78907,2.930824 5.81372,0.795662 10.92431,3.797462 16.0332,6.481033 4.44031,2.785507 9.37262,4.57396 14.06012,6.88236 4.21893,1.99959 8.57855,3.69184 12.90857,5.4335 l -6.66455,4.49293 c -4.32019,-1.83061 -8.78089,-3.32994 -12.96009,-5.4676 -4.72088,-2.26621 -9.66711,-4.07743 -14.07189,-6.959782 -5.04963,-2.636826 -10.09772,-5.655296 -15.89881,-6.100449 -5.7511,-0.251534 -11.38611,-0.945412 -16.73175,-3.267593 -6.49982,-2.292969 -13.0437,-4.481277 -19.65246,-6.436508 -4.78626,-1.257561 -9.63746,-2.563126 -13.63642,-5.608398 -3.10248,-3.711121 -5.17193,-7.636528 -5.70465,-12.573761 -0.19409,-6.079338 0.56751,-12.098358 0.87915,-18.164841 0.31079,-6.233337 1.20923,-12.439159 0.68457,-18.690048 -0.4563,-6.91667 -1.65399,-13.71149 -2.79309,-20.54486 -1.22266,-7.32645 -2.3045,-14.66818 -3.08002,-22.0561 -1.11114,-6.228991 -2.5697,-12.28233 -0.48675,-18.483059 l 7.93854,-3.288242 z"
- id="path1914"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 682.45996,-25.166901 c -2.31457,-4.035149 -1.47473,-3.19372 0.33179,-7.239429 4.11743,-6.952591 11.37292,-9.313061 18.7738,-8.11739 5.73365,1.806999 11.23993,-0.681969 17.23725,0.04506 23.35284,-6.840088 1.79034,3.677601 21.1972,-7.426861 4,-3 6.96906,-3.415539 12.7641,-4.098888 2.6825,-0.50185 5.20197,0.06368 7.7981,0.568031 l -6.79761,4.659599 c -2.49683,-0.430061 -4.93164,-0.893391 -7.49237,-0.417431 -16.23248,2.005081 -13.45429,4.498718 -18.81214,6.121571 14.38477,-8.392921 -8.73303,7.057358 -15.23632,5.278931 -5.98011,-0.831192 -11.39759,1.350739 -17.22376,-0.111813 -16.44092,-1.904709 3.11768,-4.795067 -4.77624,0.15646 -1.60944,3.55743 -2.90943,2.377491 -0.79749,5.520823 l -6.96631,5.061338 z"
- id="path1918"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 760.31207,-51.441441 c 2.78522,1.009209 5.79676,1.323311 8.64869,2.611752 3.97253,3.320549 7.94519,6.297459 12.57415,8.660168 4.56385,2.221161 9.42932,2.689743 14.41187,2.92514 5.52698,0.513599 10.95276,1.1049 16.37689,2.272713 5.32208,0.764229 10.38202,2.33609 15.53546,3.778809 5.6275,0.48411 10.01618,2.22916 13.85352,6.31637 0.93396,1.03367 1.05084,2.457911 1.56775,3.666939 l -7.24335,3.87648 c -0.42712,-1.09536 -0.41736,-2.460508 -1.31934,-3.383339 -3.88922,-3.71348 -7.98089,-5.39282 -13.44421,-5.933969 -5.14929,-1.47303 -10.28723,-2.795132 -15.57172,-3.708471 -5.37811,-1.191349 -10.82293,-1.67597 -16.30725,-2.158659 -5.08276,-0.229942 -10.00183,-0.861149 -14.61139,-3.206009 -4.61078,-2.42094 -8.58514,-5.499882 -12.7063,-8.645641 -2.77081,-1.054749 -6.14166,-1.578819 -8.73114,-2.010941 l 6.96637,-5.06134 z"
- id="path1920"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 842.1344,-23.157169 c 3.26575,2.61006 3.5047,6.187809 3.90973,10.083989 -0.22699,4.91383 -0.70227,9.81317 -0.79022,14.74727 0.28967,5.3569 -0.58765,10.462489 -2.36945,15.47499 -1.95972,4.207159 -2.91119,8.736071 -3.47443,13.307909 -0.16235,5.398911 1.92231,10.52433 3.27491,15.687412 1.71984,6.257618 2.76538,12.669579 3.24859,19.133827 0.43207,6.059631 -1.45215,11.614891 -4.01446,16.996124 l -7.43604,3.305588 c 2.81714,-5.179504 4.65216,-10.64695 4.34625,-16.633041 -0.44195,-6.42997 -1.38537,-12.820538 -3.11718,-19.04068 -1.45014,-5.201988 -3.67585,-10.329018 -3.60682,-15.79549 0.48841,-4.68651 1.46442,-9.262339 3.40522,-13.59174 1.89203,-4.878819 2.8717,-9.901489 2.52124,-15.17843 0.10333,-4.90174 0.39703,-9.78604 0.76214,-14.66073 -0.36035,-3.54787 -0.36487,-6.990119 -3.62585,-8.775661 l 6.96637,-5.061338 z"
- id="path1922"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 841.38971,78.52166 c -1.11615,5.89357 -7.98706,7.273743 -12.82166,9.960487 -5.4552,0.975502 -10.41796,1.135559 -15.41528,3.683846 11.15698,-7.924706 -2.35815,3.440178 -5.13922,7.204018 -4.62176,6.146719 -11.1059,9.182449 -17.72363,12.935069 -2.10718,1.13932 -4.38147,1.0748 -6.65625,1.27614 l 6.66003,-4.81535 c 2.18268,-0.30083 4.38135,-0.36224 6.3573,-1.5526 -5.388,3.23035 -2.86371,2.58535 3.9043,-4.63236 5.00213,-6.781745 11.05682,-11.65029 18.77649,-15.407447 5.09839,-2.430305 10.27691,-2.280235 15.65667,-3.771118 -0.89068,0.549065 -4.85656,2.940643 -1.27111,-0.971436 l 7.67236,-3.909248 z"
- id="path1924"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 845.16486,-10.02519 c 3.43566,1.54551 6.24841,4.65427 9.33599,6.95737 4.08942,4.79506 2.61182,11.03201 2.02094,16.82946 -1.00775,5.41472 -1.10315,10.91701 -1.17108,16.40491 -0.32849,4.826431 0.49212,9.604351 0.60077,14.39439 -2.47107,6.778351 -8.04859,11.757492 -12.29645,17.472851 -0.28736,0.435478 -0.57471,0.87096 -0.86206,1.306431 L 838,66.094483 c 0.28845,-0.44857 0.57696,-0.897141 0.86542,-1.345703 4.10229,-5.654251 7.06872,-10.106678 9.83264,-16.586128 -0.0535,-4.7616 -0.94953,-9.513462 -0.59052,-14.315693 0.0977,-5.531279 0.23834,-11.084768 1.22229,-16.543829 0.6117,-5.556589 2.15882,-11.417619 -1.72662,-16.005009 -2.94824,-2.28668 -5.73035,-5.65077 -9.40466,-6.26197 l 6.9663,-5.06134 z"
- id="path1926"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 826.83124,-30.39484 c 4.22448,-0.192339 5.09369,2.07025 6.22333,5.69257 -1.06794,6.217981 -8.08686,7.85902 -13.59485,8.69767 -6.23456,0.8013 -12.31195,2.40283 -18.48297,3.508301 -4.7818,0.50332 -9.48035,1.650479 -14.16431,2.635859 -0.61871,0.21159 -5.64935,-0.05301 -4.80438,8.26597 0.27057,0.54581 0.49865,1.11073 0.74798,1.66609 L 778,2.75011 c -0.004,-0.00865 -0.73059,-1.65607 -0.77307,-1.65563 -0.9436,-8.27243 5.91577,-11.29411 13.87982,-14.27409 4.7096,-0.92521 9.40613,-2.0659 14.19641,-2.56078 6.14453,-1.152381 12.23981,-2.550839 18.43103,-3.475171 9.51862,-1.680799 -0.45257,3.755501 4.4469,-2.940929 -1.04217,-3.60685 -1.90942,-5.288429 -5.99414,-4.86412 l 4.64429,-3.374229 z"
- id="path1932"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 682.46307,-21.431259 c -0.12658,-3.40843 1.35144,-5.347502 3.69318,-7.585861 5.15137,-3.69223 10.48584,-4.748878 16.59021,-5.191288 4.96954,0.723198 9.41016,3.436357 13.87048,5.651848 5.3028,2.71195 10.92139,4.615921 16.75617,5.74321 4.94171,0.64484 9.93512,0.431992 14.88757,0.82999 6.64087,1.50392 13.42633,2.356831 19.98206,4.13266 5.32007,2.0885 9.34686,5.94672 12.69995,10.43763 1.43841,2.37088 1.5943,5.00706 1.85034,7.67005 l -4.81152,2.45295 c -0.19763,-2.57971 -0.29853,-5.1015 -1.71906,-7.39166 -3.45147,-4.28058 -7.0459,-8.27916 -12.41009,-10.20365 -6.56275,-1.76145 -13.31879,-2.63011 -19.98371,-3.98123 -4.96051,-0.2922 -9.96215,-0.121571 -14.89245,-0.86182 -5.83887,-1.25622 -11.51386,-3.15267 -16.84827,-5.875509 -4.41186,-2.165932 -8.83008,-4.875422 -13.771,-5.38072 -3.37359,0.294788 -16.24847,3.894239 -7.07244,-0.04936 -2.28473,2.107269 -4.02735,3.617949 -3.70655,6.99659 l -5.11487,2.606171 z"
- id="path1934"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 782.53229,-4.66946 c -0.39008,6.41065 -0.37799,12.82219 0.39245,19.200191 1.51252,8.237849 3.35151,16.41673 5.05494,24.618649 1.87823,7.0359 2.27874,14.28194 2.33343,21.52425 -0.19598,9.691193 0.16065,19.355377 0.646,29.030983 0.51928,7.135147 2.68341,14.081917 1.06671,21.046617 l -5.08856,2.19451 c 2.35254,-6.71221 -0.13037,-13.796593 -0.45209,-20.937759 -0.37872,-9.65448 -0.95856,-19.290787 -0.81482,-28.96077 -0.18237,-7.204182 -0.4032,-14.441612 -2.26984,-21.45182 -1.64502,-8.230598 -3.55273,-16.407221 -5.02154,-24.672649 -0.82501,-6.313421 -0.69733,-12.636861 -0.96155,-18.986031 l 5.11487,-2.60617 z"
- id="path1946"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 749.52832,79.995628 c -5.51141,-1.837975 -10.86017,-4.324165 -16.258,-6.533287 -5.10443,-2.095688 -10.22876,-4.142998 -15.26898,-6.388893 -4.44751,-1.629776 -8.26501,-3.80917 -11.3659,-7.403309 -3.71759,-4.40276 -4.75415,-9.968819 -5.8216,-15.450108 -1.16229,-5.43536 -1.64593,-10.937931 -1.82464,-16.480732 -0.0932,-5.03511 -0.0516,-10.071129 -0.0869,-15.10689 -0.17553,-4.01775 -0.12616,-7.9062 0.90833,-11.79435 1.96319,-5.25881 5.57123,-8.37159 11.08288,-9.87126 3.48981,-0.65607 6.96466,-0.27815 10.44648,0.14213 4.30102,1.37104 8.19958,3.65866 12.46801,5.12931 4.01734,1.54848 8.12964,2.76404 12.13562,4.34724 4.97144,2.42331 9.75067,5.61835 13.02423,10.10043 2.58002,3.81613 3.27301,8.22651 4.06971,12.641459 0.71698,4.49304 0.5943,9.071499 0.58636,13.608219 0.0397,4.17783 0.0743,8.355572 0.0833,12.5336 0.002,4.047501 -0.0394,8.094723 -0.0391,12.142262 0.19092,4.283524 0.0289,8.462952 -0.95776,12.632469 -1.6034,4.795044 -7.03778,6.264168 -11.60047,6.8825 -2.55322,0.275475 -4.59692,-1.109161 -6.71783,-2.318428 l 3.23602,-2.098564 c 2.07569,1.205482 4.03974,2.496422 6.55548,2.13205 7.3808,-1.110809 1.31244,1.979515 5.09833,-2.967346 1.04431,-4.139458 1.21991,-8.294373 1.04993,-12.562771 3e-4,-4.034191 -0.0409,-8.06805 -0.0394,-12.102211 0.008,-4.20137 0.0439,-8.402538 0.0576,-12.603848 -0.05,-4.50214 0.0184,-9.035582 -0.66126,-13.49786 -0.7655,-4.375582 -1.40069,-8.75465 -3.94677,-12.538421 -3.28193,-4.40279 -7.83759,-7.70772 -12.87781,-9.92607 -4.00885,-1.5876 -8.19489,-2.62201 -12.1753,-4.29483 -4.23187,-1.52755 -8.13641,-3.82845 -12.47039,-5.04614 -3.46247,-0.40364 -6.92792,-0.70882 -10.38343,-0.01101 -0.2561,0.15734 -0.95861,-0.24321 -4.60248,5.92243 -1.03466,3.8695 -1.13269,7.71445 -0.99035,11.71708 -0.0303,5.03792 -0.0179,10.07577 0.0558,15.113311 0.16144,5.534988 0.57319,11.03042 1.7262,16.461981 1.07025,5.40049 2.11652,10.928009 5.79102,15.250008 3.11871,3.505592 6.92541,5.607281 11.31311,7.232403 4.98376,2.282211 10.07135,4.330139 15.10553,6.498711 5.40082,2.245796 10.82226,5.048889 16.56152,6.135017 l -3.26715,2.373718 z"
- id="path2006"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- <path
- d="m 776.72467,61.430309 c -2.177,-0.2812 -4.32941,-0.54921 -6.52838,-0.61224 0.096,-2.30928 -0.98145,1.947342 -0.12665,6.479858 1.0379,3.329605 0.36261,6.608704 -0.20398,9.945259 -0.50751,3.377434 1.16132,5.538422 3.1958,7.9842 2.94147,2.466415 6.54212,3.010597 10.24365,2.808121 -4.47021,4.028763 -3.77191,-4.397057 -3.27105,-8.651581 0.57507,-6.013435 1.53277,-13.23275 -3.45337,-17.470367 -0.24194,-0.104248 -0.48389,-0.208488 -0.72583,-0.31274 l 3.17053,-2.104519 c 0.25476,0.119492 0.50958,0.238979 0.76434,0.358471 4.8028,4.512417 3.9632,11.781368 3.4787,17.932026 -0.66778,6.013641 3.19678,9.985016 -3.05163,12.576653 -3.77588,-0.01461 -7.49909,-0.575752 -10.4715,-3.123322 -2.08124,-2.54274 -3.81628,-4.830284 -3.30951,-8.31295 0.55707,-3.28112 1.24915,-6.500069 0.22662,-9.777267 -1.01556,-5.581741 0.91248,-8.74828 6.6565,-10.6283 2.24499,0.107239 4.45355,0.47805 6.68976,0.522758 l -3.284,2.385941 z"
- id="path2008"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:1px" />
- </g>
- </g>
- <g
- id="layer22"
- display="inline"
- style="display:inline">
- <g
- id="g24646"
- transform="matrix(4.00794,0,0,3.05846,1738.94,-2148.82)">
- <path
- d="m -299.78821,884.94104 c -11.2077,17.72577 -34.02069,29.28235 -54.37558,20.68341 -10.4747,-4.67499 -4.50213,-25.43457 -21.0491,-14.13629 -16.01419,10.94244 -34.07871,-2.97278 -39.88995,-18.46692 -9.78833,-14.40991 -3.3075,-36.72845 5.51223,-41.94855 -8.22226,-14.37732 -15.10849,-34.73199 -6.63614,-49.86663 15.56357,-5.54834 36.081,6.37951 46.22812,-13.28064 9.87281,-11.33124 25.83487,-25.48603 41.4632,-15.71619 7.90399,11.46368 17.65387,17.48108 31.2164,10.27557 16.9013,-3.81891 39.16611,-11.91589 53.19356,2.52716 4.74927,13.30573 11.80819,26.44129 27.6036,17.3725 20.6382,-4.04309 32.23424,21.79578 29.8366,38.97028 -4.88782,14.17657 -22.11452,25.42419 -8.54654,40.74389 5.39746,13.28253 1.53802,38.151 -17.26,36.26966 -18.07486,-4.7099 -27.21221,14.8529 -44.13844,15.18127 -17.97122,4.32263 -35.9444,-7.36072 -42.35452,-24.04797 l -0.59647,-2.3324 -0.20697,-2.22815 0,0 z"
- id="path20525"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#radialGradient8101);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -402.98733,832.17139 c -9.7815,-12.6922 -15.44208,-29.46955 -14.2858,-45.4502 4.22574,-15.98376 34.66415,-3.40765 35.09628,-0.56219 -9.24631,10.99762 -31.52789,-18.83319 -29.47214,3.1853 -1.62716,14.22247 9.1272,30.27978 13.3006,39.45667 -1.54632,1.12347 -3.09262,2.24694 -4.63894,3.37042 z"
- id="path21335"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -376.49039,797.14471 c -11.30673,-16.61102 4.27671,-36.0177 20.36808,-41.40808 14.15902,-4.95123 36.42092,7.1958 41.27145,15.83405 -10.28082,5.65179 -20.79559,-12.63819 -33.06375,-12.19293 -15.19345,-7.15625 -31.41904,14.62933 -25.08658,28.59326 5.84967,6.83502 0.9632,5.61554 -3.4892,9.1737 z"
- id="path21339"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -319.19733,775.43426 c 8.40601,-21.95703 39.32529,-26.1159 58.04926,-15.56066 12.58522,7.2854 31.64825,24.78131 26.9909,38.26165 -8.23838,3.84797 -7.20375,-19.01397 -16.71989,-22.34283 -12.96886,-14.3891 -35.90907,-24.10175 -54.13414,-14.24017 -5.9621,4.34735 -6.2959,12.12347 -14.18613,13.88202 z"
- id="path21342"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -237.90907,791.6554 c -13.48096,7.61548 11.88724,-8.4806 19.04454,-7.85529 20.06553,-2.10083 38.01188,27.50531 20.20776,41.58704 -6.62549,5.44683 -13.02862,9.54699 -9.76851,7.35131 -11.19906,5.22821 15.06346,-9.44781 10.73677,-18.01831 3.12589,-20.56744 -23.63943,-34.25293 -39.81651,-22.55951 -9.11417,5.3786 -7.00043,4.41376 -0.40405,-0.50525 z"
- id="path21344"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -209.91811,835.64124 c 11.87803,9.58935 26.01008,25.9804 20.152,42.05194 -5.44144,14.1336 -32.45381,25.43896 -40.31215,20.54016 9.53958,-5.84204 25.07269,-1.2472 32.01846,-13.37513 10.73161,-14.55719 -1.91795,-33.08471 -13.86107,-42.44244 -8.95831,-2.71832 -0.39277,-3.65356 2.00276,-6.77454 z"
- id="path21346"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -224.58003,888.95746 c 2.80882,16.67846 -16.68367,27.05475 -30.95954,27.24499 -19.41328,0.14899 -40.78862,-10.55493 -45.24266,-30.95208 12.22266,-10.09314 9.64478,20.38665 23.95237,20.43261 11.39239,6.87763 33.10633,8.94233 39.95983,2.88983 8.54885,-5.86602 2.75145,-16.08624 12.29,-19.61535 z"
- id="path21348"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -301.68662,884.1546 c 17.5709,-9.96631 -7.90414,11.30853 -12.36376,15.03369 -11.39835,11.97614 -41.38874,13.22388 -43.99473,-3.31939 8.62189,-4.59809 16.42167,15.75994 29.32633,8.6654 12.05807,-2.08807 27.13382,-22.36737 31.67111,-23.75006 -1.54629,1.12347 -3.09262,2.24689 -4.63895,3.37036 z"
- id="path21350"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -352.33859,880.25787 c -10.66181,22.79493 -50.20664,20.1894 -57.03208,-4.5241 -3.75015,-13.52558 -7.37625,-35.11054 7.34442,-43.07349 2.94873,-0.23651 -10.12225,18.04309 -4.71194,26.08276 -0.9173,21.52832 19.16339,36.70606 39.54605,34.18146 5.71335,-3.67871 7.52487,-10.90594 14.85355,-12.66662 z"
- id="path21352"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -359.00149,785.51971 c -7.94028,-0.82562 -19.94269,16.86127 -21.54371,12.52204 3.41196,-7.33118 29.02771,-19.318 24.59,-14.73529 -1.01544,0.73773 -2.03085,1.47552 -3.04629,2.21325 z"
- id="path21356"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -208.41951,824.52454 c -1.81009,10.14154 0.23729,26.9411 -12.17812,30.27197 8.91109,-9.05658 -0.8262,-27.33905 12.17812,-30.27197 z"
- id="path21360"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -320.62692,876.78888 c -9.31876,9.79156 -38.45938,10.906 -41.19766,4.78253 10.57855,-4.17169 24.88476,4.23285 36.01632,-2.63635 1.72711,-0.7154 3.45423,-1.43079 5.18134,-2.14618 z"
- id="path21370"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -398.98862,844.16748 c -5.63791,-7.03473 1.861,-35.26837 6.27277,-26.37647 -5.73715,9.51404 5.30395,22.05109 -6.27277,26.37647 z"
- id="path21372"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -303.78693,788.94104 c -24.12677,-14.57483 -57.59604,-10.84894 -78.12237,8.54413 -12.15347,11.40552 -20.78781,32.91962 -12.51477,48.91278 8.30206,14.69592 21.2142,26.60016 34.91653,36.18219 18.52677,5.80292 40.76239,5.8869 58.81912,-1.82837 18.30289,-11.66931 33.41462,-34.46912 25.65464,-56.74823 -3.99124,-18.92835 -16.34397,-36.78809 -36.70139,-39.96899 -5.24011,-1.32044 -10.63562,-2.08051 -16.044,-2.09351"
- id="path22190"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#radialGradient8118);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -355.77017,808.94104 c -16.87442,-4.67041 -30.15064,-31.98614 -8.38217,-40.22498 17.96743,-15.68573 38.22201,4.24482 55.93936,4.00586 14.40134,-6.45367 32.16711,-15.61218 47.03143,-5.15875 18.5562,7.62329 26.18392,32.15827 6.58614,43.9231 -24.51302,21.19006 -60.50716,18.82977 -89.47026,9.56225 -7.80216,-3.32873 -11.54679,-11.57757 -14.13654,-19.10748"
- id="path23008"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#radialGradient8123);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -229.20216,890.94104 c -9.95003,15.42987 -34.09954,12.34058 -49.2394,7.96655 -16.19226,-9.36035 -19.18359,-29.43573 -19.68838,-46.40954 -2.76523,-15.30573 -8.10074,-36.75251 8.12857,-46.38831 16.28634,-9.02039 34.9917,-3.04266 52.33,-5.34302 17.08401,-11.80212 30.35144,5.40229 29.98193,21.89832 0.91018,24.0047 -29.40792,33.01898 -37.40678,49.95739 8.45975,3.21882 26.32217,4.89515 15.89406,18.31861 z"
- id="path23826"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient8128);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- </g>
- </g>
- <g
- id="layer21"
- display="none"
- style="display:none">
- <g
- id="g20456"
- transform="matrix(0.847349,0,0,0.847349,-195.7578,-42.87657)">
- <path
- d="m -112,904.09448 c -2.58744,0.6521 -13.7986,-8 -21,-8 -6.72012,0 -13.65811,-2 -21,-2 -10.10316,0 -19.78154,0 -30,0 -12.02331,0 -22.85286,-1 -35,-1 -12.86507,0 -25.4637,-2 -38,-2 -8.67566,0 -22.60187,1.84082 -29,7 -7.90491,6.37421 10.97678,9 14.47644,9 26.00656,0 52.93065,1 79.52356,1 17.93507,0 35.94823,0 54,0 6.14961,0 15.41198,2.56702 20,-1 4.48426,-3.48633 -3.20381,-11.40124 -5,-12"
- id="path17971"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#b99c79;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -120,896.09448 c -5.18856,-1.22253 -11.36861,-9.36859 -17,-15 -5.8927,-5.8927 -8.80621,-18.60498 -10.45192,-26.55035 -1.71461,-8.27802 -5.59355,-13.95142 -13.54808,-16.96509 -4.90417,-1.85797 -9.01488,-9.5044 -12,-13.48456 -4.03679,-5.38238 -5.8577,-6.28638 -7.43962,-13 -1.47635,-6.26562 -8.63458,-10.76855 -13.56038,-12 -4.63402,-1.15851 -11.27116,0 -16,0 -3.90378,0 -7.87418,12.74835 -9,15 -3.80571,7.61139 -7,5.09351 -7,15 0,9.16553 -2.37489,9.03119 -9,14 -4.69961,3.52472 -7.90057,8.27118 -11.41501,12 -4.39909,4.66742 -6.8035,10.70783 -12.58499,15.47223 -4.17331,3.43921 -9.26959,5.79736 -13,9.52777 -3.15976,3.15973 -4.59555,5.57715 -7.40271,9 -4.53391,5.52826 6.26923,2.53339 8.40271,2 4.22626,-1.05658 11.05987,-1 15,-1 8.53882,0 17.22183,2 26,2 6.92438,0 13.84843,0 22,0 9.50096,0 19.00192,0 28.50288,0 10.18596,0 17.38753,1.27741 26.49712,-1 4.90628,-1.22656 14.91875,2 20,2 4.40485,0 9.64672,0.48505 13,3 z"
- id="path18783"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#fdffbb;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -161.55897,874.09448 c -1.73926,2.60889 -10.02474,-9.45691 -11.99614,-11 -7.25097,-5.67566 -11.42131,-7.58441 -19.99357,-10 -5.2566,-1.48126 -11.33869,1.68604 -15.99485,3 -6.99086,1.97284 -8.71938,-3.47418 -13.45647,6 -2.83936,5.67871 -6.87866,8.51251 -10.5358,14 -0.52278,0.78443 -1.33291,1.33331 -1.99936,2 -4.42585,4.42725 -8.94059,7.71619 -12.46484,12 -0.73769,0.89667 9.43133,2 10.46548,2 5.99807,0 11.99614,0 17.99421,0 0.84677,0 1.69354,0 2.54031,0 7.45036,0 16.94263,1.35248 24,-1 4.58049,-1.52685 24.08504,1.77124 29,3 5.75507,1.43878 10.26843,-2 16.43652,-2 9.77311,0 6.84127,-1.5741 -0.43652,-4 -6.22075,-2.07361 -7.99979,-6.99975 -12,-11 -0.79687,-0.79687 -1.03931,-2 -1.55897,-3 z"
- id="path19593"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#c2c38f;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -259.52744,894.09448 c -3.51116,0 -10.45745,0 -15.47256,0 -4.54288,0 -7.78699,1.22955 -8.51971,-3 -0.39228,-2.2644 7.18954,-5.47247 8.51971,-6 5.06543,-2.00879 5.22617,-5.22619 8,-8 3.82407,-3.82403 5.02478,-9 12,-9 3.8873,0 2.95046,-7.58929 6,-10 4.39581,-3.47491 5.53024,-6.28735 11.46549,-8 2.25119,-0.6496 4.36463,-8.28863 6.53451,-10 5.44969,-4.29822 9,-5.8316 9,2 0,7.04047 -3.64722,7.41181 -10,9 -3.82872,0.95715 -7.26369,3.75458 -11,5 -5.09579,1.69861 -8.01744,7.01746 -11,10 -3.30045,3.30048 -6.31152,6.31152 -10,10 -3.19391,3.19391 -3,5.75611 -3,12 0,2.00659 5.90085,5.09479 7.47256,6 z"
- id="path19595"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#c2c38f;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -145,866.09448 c -1.72639,-0.88678 -9.79105,-2.79101 -13,-6 -2.25404,-2.25403 -5.53384,-5.53387 -8,-8 -3.70766,-3.70764 -4.75426,-10.67352 -5.55576,-15 -0.0664,-0.35864 -0.29615,-0.66669 -0.44424,-1 -1.89291,-4.26092 -2.92903,-11.21289 -2,-14 0.95067,-2.85199 9.46745,8.46747 10,9 4.39664,4.39661 10.48602,8.35626 16,13 0.36057,0.30365 0.66667,0.66669 1,1 5.48428,5.48432 5.75848,13.48303 2,21 z"
- id="path20403"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#c2c38f;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -152,881.09448 c -1.41557,-1.80865 -8.94191,-3 -15,-3 -4.69234,0 -9.27278,-3 -15,-3 -1,0 -2,0 -3,0 -4.64053,0 -6.10098,1.03363 -9,2 -3.36075,1.12024 -5.23721,2.78485 -9.54546,4 -0.036,0.0101 -7.78018,-5 -10.45454,-5 -4,0 -8,0 -12,0 -5.7653,0 -8.07764,1.54199 -11.53451,5 -2.89948,2.90039 -6.64798,4.1825 -9.46549,7 -4.70387,4.70386 -0.43742,3.14581 3,2 4.93095,-1.64368 9.71721,-4 14,-4 7.5229,0 11.61667,0.87219 18,3 2.16383,0.72125 5.25644,5.07422 8.45454,6 7.47741,2.16449 10.45847,-4.46185 13.9955,-8 2.42057,-2.42132 13.35653,-3 17.54996,-3 5.91898,0 8.74661,0.57623 14.43974,2 3.84919,0.96259 8.00936,2.83966 11.99614,4 4.4887,1.3064 -4.62838,-6.77508 -6.43588,-9 z"
- id="path20405"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient20478);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -268,914.25684 c -4.4675,-0.16278 -9.76108,-0.6543 -14.22064,-0.80127 -6.45426,-0.20539 -9.00885,-2.29364 -12.16352,-7.75763 -2.03265,-10.74658 20.38416,-18.60346 6.81162,-10.91931 C -279,886.09448 -275,891.09448 -271,888.09448 c -1,-4.55249 4.26367,-13.40356 9.55765,-17.51599 4.66934,-3.70605 7.20611,-9.22589 10.05725,-14.33319 3.808,-5.25928 8.78354,-9.53882 13.29954,-14.17902 4.94123,-4.51092 7.29972,-8.90142 7.78546,-15.42669 -0.32994,-6.04883 2.44046,-11.05463 5.14151,-16.22956 4.86924,-9.58398 13.41376,-14.90417 24.09557,-16.52099 7.26,-0.86151 14.38252,-0.56824 21.1637,2.16992 5.76278,3.52051 7.35498,8.64203 7.64412,15.0188 0.65399,4.72406 -0.15995,6.12488 3.2552,9.01672 2.26987,5.38507 7.00226,10.70923 11,15 5.56538,2.83045 8.2034,5.09687 12,10 3.20149,5.1579 5.51733,11.5412 6.59782,17.42743 0.77365,5.2909 1.99251,9.90674 4.40218,14.57257 2.77507,5.71875 8.75345,9.75897 14,13 5.47835,3.4372 10.3334,8.56317 12.46964,14.56323 0.1199,9.53742 -8.39631,10.844 -16.66467,11.99347 -6.65458,0.38532 -13.32864,0.30231 -19.99189,0.26551 -9.19241,-0.47626 -18.36142,-1.20948 -27.58357,-1.32672 -10.26541,-0.41571 -20.59017,0.46392 -30.83001,-0.37488 -9.88301,-0.96369 -19.81712,-0.92511 -29.73481,-0.97553 -9.50138,-0.0379 -20.16347,0.60352 -29.66469,0.56549 -4.26355,0.0115 -7.73743,-1.36236 -12,-1.29962 l 8.52875,-5.41095 c 4.13989,0.0636 8.97454,-0.0923 13.11554,-0.081 9.53753,-0.0365 19.07328,-0.0325 28.6112,-0.009 9.91214,0.0858 19.84191,0.18256 29.72035,1.08014 10.23414,0.52332 20.49154,0.16645 30.7354,0.36011 9.29099,0.159 18.53081,0.76135 27.80213,1.14947 6.70304,-0.0582 13.42153,0.0426 20.10843,-0.48626 12.54477,-2.3689 -6.51377,9.46173 1.14825,-2.23999 -1.98112,-5.75098 -5.74785,-9.25592 -10.93614,-12.5694 -5.36533,-3.38483 -10.41513,-6.90906 -13.32855,-12.75555 -2.51275,-4.75141 -5.12149,-9.42914 -5.92193,-14.83844 -1.07981,-5.74793 -1.93209,-11.76465 -5.08595,-16.79181 -3.77547,-4.78174 -8.4008,-7.66327 -13.73703,-10.61078 -3.87823,-4.37231 -6.72104,-9.03821 -8.96744,-14.42719 -3.76841,-2.98663 -8.42807,-4.50567 -8.902,-9.8269 0.003,-6.01776 -1.41924,-10.72784 -6.91608,-13.9339 -6.75051,-2.19696 -13.54109,-2.92883 -20.67072,-1.78369 -11.81434,2.21564 6.99096,-8.5257 -5.43385,5.67895 -2.72513,4.93305 -5.50069,9.83655 -5.40819,15.66138 -0.21646,6.82068 -2.31134,11.56696 -7.59416,16.1875 -4.38885,4.65943 -9.18948,8.97547 -13.18704,13.97827 -2.97908,5.07782 -5.44969,10.75482 -9.97376,14.64325 -5.24734,4.17804 -11.15283,7.54291 -15.62802,12.59308 -5.39676,6.73102 2.32892,5.51575 -1.07919,13.02185 -8,3 -15,3.41266 -16.5882,8.25952 3.14582,4.80793 5.18177,6.63294 11.28858,6.74048 4.6084,0.0572 8.87768,1.23798 13.48504,1 L -268,914.25684 z"
- id="path17850"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -260.92084,870.72846 c -2.02697,2.00555 -3.36123,4.42138 -4.38266,7.05139 -1.14688,3.66076 -1.31848,7.15442 0.52079,10.51977 2.59246,2.33563 5.78537,2.97125 9.13156,3.41431 8.3724,-2.84363 -3.43381,3.50378 5.20251,-2.30121 4.3953,-2.98621 8.25847,-6.61059 11.96761,-10.39197 3.62863,-3.69305 7.17676,-7.47461 10.6242,-11.33947 2.49032,-2.86524 4.75713,-5.91346 7.20516,-8.81037 1.94475,-2.28619 4.14058,-4.3363 6.03671,-6.64807 0.47028,-2.68842 -0.26611,-5.41821 -0.78532,-8.05853 -1.09424,-3.72779 -3.58673,-6.65522 -6.11055,-9.50751 -2.32933,-2.51849 -3.20027,-5.84393 -4.40087,-8.96808 3.97815,-3.83856 2.73832,-3.20532 2.2979,2.46057 0.2047,3.5979 0.52274,7.15857 1.15881,10.70679 0.95626,3.62628 1.71239,6.59772 -0.58748,9.60193 -5.45587,3.86731 -11.97345,5.25006 -17.84675,8.28723 -4.71897,2.5896 -4.59062,2.59277 -9.06494,5.63049 -1.60422,1.0556 -3.18424,2.14514 -4.75411,3.24951 L -258,867.09448 c 1.55771,-1.11548 1.54214,-1.74377 3.14749,-2.79248 11.43854,-8.01794 6.00452,-3.41437 17.96033,-10.76721 3.54925,-1.80737 18.03969,-7.703 8.70138,-3.07861 2.77915,-2.57807 1.90039,-5.50641 0.96601,-9.06098 -0.64718,-3.56213 -0.972,-7.14013 -1.25147,-10.74749 0.13609,-5.30488 1.70923,-8.05231 7.34178,-7.63514 1.14438,3.0838 2.01566,6.35657 4.25688,8.85968 2.46697,2.97388 5.11768,5.90662 6.27277,9.67365 0.64355,2.73712 1.24602,5.56067 1.07163,8.38464 -1.84574,2.42407 -4.07328,4.55237 -6.12368,6.84473 -2.54393,2.86896 -4.75554,6.01446 -7.33209,8.85992 -3.5054,3.87287 -7.02356,7.73914 -10.73041,11.41523 -3.6882,3.75219 -7.43167,7.45654 -11.8079,10.42053 -4.39167,3.0697 -9.04872,7.07336 -14.49955,7.37317 -3.51126,-0.53461 -6.83102,-1.18049 -9.45919,-3.75751 -1.87293,-3.53143 -1.82941,-7.16376 -0.69117,-11.01319 0.88828,-2.64093 2.17069,-4.97149 3.93021,-7.13879 l 5.32614,-2.20618 z"
- id="path17859"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -137.18397,883.89996 c -0.71708,-4.91888 -2.8639,-9.28698 -6.18992,-12.90954 -3.83023,-2.45728 -7.5796,-4.94049 -11.15185,-7.77735 -4.28468,-2.58917 -7.98564,-5.64447 -11.24669,-9.42743 -3.29219,-3.77557 -5.59212,-8.16412 -7.52319,-12.74572 -1.58413,-4.56409 -2.9605,-9.19129 -5.45507,-13.34162 -7.76187,-4.88037 -0.55732,-5.89935 -2.3506,-2.10394 -1.16819,3.83746 -1.95424,7.77624 -3.07667,11.63019 -0.75511,3.5719 -2.47194,6.82019 -4.26398,9.96686 -1.92815,2.99707 -1.13695,4.01965 1.27011,6.08142 2.64862,1.5971 5.82285,1.24981 8.70694,2.0346 3.70085,2.13172 7.24167,4.59351 10.36022,7.50977 2.28331,2.07349 3.67077,4.81909 5.53093,7.23108 2.0843,3.09802 3.8999,4.98553 7.51164,5.83807 3.19092,0.50537 9.20442,4.8938 5.05722,3.35071 -3.16712,-0.21051 -6.36492,0.37329 -9.54457,-0.20007 -3.6429,-1.02429 -5.5127,-3.0791 -7.65798,-6.2049 -1.91607,-2.35809 -3.38296,-5.0224 -5.51173,-7.21106 -3.02704,-2.92431 -6.4859,-5.38727 -10.2637,-7.26397 -2.98886,-0.50135 -6.19205,-0.45661 -8.83119,-2.1507 -2.59267,-2.14917 -3.65453,-3.54657 -1.61123,-6.77734 1.84699,-3.11176 3.5515,-6.35541 4.40897,-9.89655 1.12311,-3.85846 1.95235,-7.78607 3.01283,-11.65698 2.18575,-5.74213 8.34958,-8.49658 11.82553,-2.87067 2.6061,4.15497 3.77865,8.88831 5.44883,13.48145 1.98318,4.49987 4.22113,8.83435 7.45132,12.59259 3.22516,3.74957 7.0433,6.64178 11.17656,9.34314 3.62588,2.80273 7.51533,5.16571 11.24988,7.78888 3.38575,3.74353 5.51506,8.21606 6.31325,13.24877 0.48812,3.49878 0.31352,6.83149 -0.45556,10.27149 0,0 -3.80189,-4.25159 -4.1903,-7.83118 z"
- id="path17861"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -209.63565,847.86084 c 1.14227,1.05127 2.5406,3.03095 3.84424,4.35504 2.69894,1.79859 5.90188,1.78577 9.03094,1.91657 3.50829,0.45813 6.65287,-0.71942 9.87358,-1.84271 0.15944,0.007 0.3189,0.0138 0.47834,0.0207 l -4.51093,3.29651 c -0.13019,0.0149 -0.26038,0.0298 -0.39056,0.0447 -3.20423,1.15247 -6.33913,2.09296 -9.80732,1.62677 -3.28002,-0.125 -6.6132,-0.16156 -9.38761,-2.13708 -1.44344,-1.54602 -2.66019,-3.3075 -4.27805,-4.65784 l 5.14737,-2.62268 z"
- id="path17863"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -233.14055,875.40759 c 2.62326,-0.007 5.12261,-1.1936 7.62894,-1.91827 3.48706,-0.72077 7.07141,-0.59277 10.61355,-0.63129 2.81743,-0.0277 5.5038,0.86212 8.19297,1.57935 3.05172,0.68469 5.33803,2.77978 7.69551,4.70184 0.38602,0.34924 0.7388,0.73206 1.10818,1.09808 l -4.61374,2.82801 c -0.3677,-0.35443 -0.72218,-0.7226 -1.10313,-1.0633 -2.32455,-1.86731 -4.61545,-3.80987 -7.61909,-4.41156 -2.63282,-0.73443 -5.27743,-1.57769 -8.04238,-1.54504 -3.51948,0.0171 -7.08459,-0.0435 -10.53618,0.73907 -2.62533,0.80701 -5.22001,1.96387 -7.99833,2.01874 l 4.6737,-3.39563 z"
- id="path17867"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -198.48505,880.07275 c -7.94053,4.82984 1.72741,-1.03149 6.05957,-3.47821 4.28206,-1.72265 8.79099,-2.0924 13.34923,-2.24121 4.34075,-0.34594 8.63335,0.40937 12.92818,0.89636 3.69345,0.40687 7.25717,1.55756 10.83298,2.51917 2.21057,0.80701 6.11346,1.03503 8.31509,1.85858 l -6.23148,3.70178 c -2.19799,-0.83294 -4.36093,-1.76373 -6.60108,-2.4837 -3.52974,-1.00934 -7.05672,-2.14404 -10.73665,-2.45416 -4.21418,-0.56159 -8.435,-1.23694 -12.70459,-0.96753 -4.58974,0.19116 -9.17484,0.68762 -13.42002,2.57776 8.60334,-5.09235 -3.52296,2.54742 -6.46493,3.46679 l 4.6737,-3.39563 z"
- id="path17869"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -148.50113,879.40631 c 1.33515,2.3371 2.94281,4.50653 4.66179,6.57044 1.21697,1.59674 2.9267,2.69647 4.49538,3.89831 l -4.8142,2.88013 c -1.47972,-1.25159 -3.12883,-2.40845 -4.3665,-3.95679 -1.53006,-1.84717 -3.35795,-4.54901 -4.65017,-5.99646 l 4.6737,-3.39563 z"
- id="path17871"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -148.62518,892.79877 c -3.09282,-1.67182 -6.42597,-3.03315 -9.81703,-4.1247 -5.55385,-1.62921 -11.35325,-1.86072 -17.10109,-1.96417 -4.59024,-0.45917 -8.88871,0.45349 -13.21441,1.86505 6.07184,-3.90826 -0.006,0.1535 -3.23138,2.9856 l -5.06306,2.12164 c 3.74766,-3.40491 7.92265,-6.27332 12.54158,-8.41785 4.35916,-1.3042 8.65406,-2.11664 13.23741,-1.64355 5.76899,0.16589 11.56016,0.58148 17.15396,2.10589 3.41977,1.11682 6.78065,2.72315 10.16772,3.6764 l -4.6737,3.39569 z"
- id="path17873"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -191.15407,888.07019 c 0.54708,2.84503 -1.06598,4.97882 -3.06593,6.90985 -5.13802,3.51294 -9.68549,4.26367 -15.4856,2.74286 -1.62613,-0.52264 -2.79133,-1.70422 -4.02321,-2.81097 l 4.66839,-2.86005 c 1.18861,1.02771 2.25692,2.16492 3.82963,2.6123 4.03887,0.99738 14.2278,-1.36499 5.88661,2.29059 1.86322,-1.48791 3.36644,-3.39972 3.51641,-5.48895 l 4.6737,-3.39563 z"
- id="path17875"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -214.60394,893.46521 c -2.59098,-1.21344 -4.94429,-3.091 -7.56391,-4.41803 -3.90126,-1.65375 -8.12555,-2.20258 -12.32968,-1.96533 -4.85119,0.72113 -9.32251,2.72333 -13.63711,4.96576 7.71177,-4.7381 0.3287,0.27801 -1.42862,1.47253 l -4.91019,1.92981 c 3.46402,-2.29114 6.70094,-4.89374 10.47269,-6.71393 4.33447,-2.20941 8.81665,-4.18554 13.69942,-4.75726 4.30514,-0.21289 8.67459,0.28546 12.62309,2.1109 2.55813,1.33325 4.97536,3.4447 7.74801,3.97992 l -4.6737,3.39563 z"
- id="path17877"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -121.96709,898.79681 c -3.25783,-0.0197 -6.48504,-0.30639 -9.73266,-0.53692 -4.09581,-0.40784 -8.11466,-1.38654 -12.19681,-1.948 -5.87689,-0.47119 -11.78176,-0.35101 -17.67253,-0.35632 -5.63059,-0.005 -11.26113,0.0226 -16.89176,0.006 -5.60878,-0.0361 -11.20105,-0.0925 -16.79438,0.33557 -2.02335,0.16436 -4.05123,0.25995 -6.07805,0.36633 l 4.44058,-3.24805 c 1.95419,-0.0365 3.90817,-0.0839 5.85818,-0.2323 5.5859,-0.40271 11.18384,-0.26135 16.78183,-0.27893 5.62043,-0.0158 11.24077,0.009 16.86112,0.0168 5.95509,0.0162 11.92171,-0.011 17.86826,0.28399 4.05368,0.57935 8.04734,1.57544 12.12621,1.97449 3.36273,0.25995 6.732,0.48175 10.10372,0.22131 l -4.67371,3.39563 z"
- id="path17879"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -213.67372,896.09448 c -4.89462,-0.29693 -12.06834,-0.21417 -16.96974,-0.15253 -4.1361,0.24543 -8.22547,-0.3703 -12.35175,-0.52417 -4.37021,-0.1 -8.74235,-0.0844 -13.11352,-0.0781 -5.32141,-0.0749 -10.63138,-0.10913 -15.94168,0.26813 -4.79795,0.39892 -9.57108,0.35443 -14.35849,-0.0407 -0.23603,-0.0143 -0.47202,-0.0286 -0.70801,-0.043 l 4.43008,-3.17291 c 0.23514,0.0178 0.47028,0.0355 0.70542,0.0532 4.69485,0.46527 9.38403,0.55108 14.09619,0.1358 5.36541,-0.31628 10.72888,-0.36957 16.10386,-0.34424 4.37992,0.0122 8.76003,0.002 13.13945,0.0781 4.06191,0.15057 8.0824,0.71063 12.15168,0.65033 5.07537,0.0661 12.4205,0.0525 17.49023,-0.22559 l -4.67372,3.39563 z"
- id="path17881"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -211.82678,826.00104 c 4.87269,-0.40412 6.51632,1.78375 8.04983,6.17718 0.89899,2.96595 0.96888,6.07715 1.15151,9.14478 l -3.18073,1.65881 c -0.16575,-3.05536 -0.23195,-6.14685 -1.04638,-9.1181 -1.47213,-4.41596 -3.31694,-6.19104 -8.05864,-5.62171 l 3.08441,-2.24096 z"
- id="path17919"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -193.88248,823.99597 c 3.35318,-0.0137 6.59667,0.8194 9.84342,1.55762 l -2.67604,1.85492 c -3.38333,-0.73926 -6.77738,-1.55121 -10.26747,-1.16016 l 3.10009,-2.25238 z"
- id="path17937"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- </g>
- </g>
- <g
- id="layer20"
- display="none"
- style="display:none">
- <g
- id="g16114">
- <path
- d="m -485.64377,715.10876 c 5.12271,6.74305 19.35874,35.97303 23,56 3.36496,18.50727 1.09244,45.15918 -11.4273,59 -22.29914,24.6521 -56.96384,30.28974 -88.57273,35.57093 -23.5459,3.93408 -37.6842,1.57062 -58.45819,-9.57093 -30.4884,-16.35162 -39.54181,-55.36596 -39.54181,-86.56463 0,-36.68219 11.10504,-55.20966 43,-73.43537 32.24329,-18.42468 50.83496,-21 89,-21 15.87177,0 43.00003,5.89679 43.00003,26.57496"
- id="path3680"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#5fabe8;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -660.6438,790.10876 c -3.54156,-9.83856 -5.99328,-28.51678 -1,-41 0.66669,-1.66668 1.33331,-3.33331 2,-5 3.53797,-8.84484 5,-12.4281 5,-22 0,-8.59271 11.72919,-15.77307 17.51923,-20.42504 5.00317,-4.01984 15.26959,-6.26752 22.48077,-7.57496 7.53613,-1.36627 12.10736,-7.32501 19,-10 11.8432,-4.59619 26.72345,-0.93084 39,-4 6.13342,-1.53332 13.50476,-1 20,-1 9.32453,0 20.99091,-0.0776 29.58502,3 7.5105,2.68952 15.95221,16.79071 22.41501,23 2.34738,2.25531 -30.00003,-14.88415 -30.00003,5 0,7.56519 -19.96966,-16.49786 -26,7 -5.17816,20.17743 -8.61816,13.57093 -26,24 -3.01813,1.81092 -13.39599,-8.604 -27,5 -1.77991,1.77991 -4,3.05683 -6,4.58527 -8.66632,6.62293 -23.86139,29.41473 -34,29.41473 -9.18066,0 -13.35437,1.54938 -24,4.51545 -3.44098,0.95874 -8.81952,0 -12.46844,0"
- id="path5306"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16138);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -458.64377,774.10876 c 0,4.09675 0,14.84125 0,22 0,6.95008 -2.39273,15.63269 -5,21.50312 -3.65173,8.2221 -13.85611,15.35302 -20,21.49688 -7.68405,7.68403 -16.75467,9.75812 -26,15 -8.41373,4.77039 -18.39725,7.5589 -27.00003,11 -9.15924,3.6637 -23.78394,0.10907 -33.56287,1.51343 -8.66864,1.24494 -16.16601,-1.69568 -23.43713,-3.51343 -7.79419,-1.94854 -11.75818,-17.2868 -12,-19 -0.67651,-4.79473 27.89929,13.21033 33.55213,-5 5.40344,-17.40698 9.40441,-3.67388 25.44787,-1 15.74573,2.62427 -0.93842,-31.62567 27,-13 9.3797,6.25312 -0.34857,-23.17431 22.00003,-12 7.4516,3.72583 24.74036,-24.55285 27,-31 0.2724,-0.77722 10.15616,-5.54156 12,-8 z"
- id="path6122"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16140);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -593.6438,861.10876 c 7.91449,0 -6.97778,-39.01098 -9.49304,-41.55438 -0.88147,-0.89129 -1.00464,-2.29706 -1.50696,-3.44562 -6.35132,-14.52282 -13.19128,-22.89288 -6,-40 5.35596,-12.7409 -2.13812,-25.83154 -7.57312,-38 -5.37988,-12.04498 -26.54761,-16.88244 -37.42688,-18.45996 -23.4317,-3.39758 -0.10889,49.99952 2,52.45996 17.99085,20.98926 20,26.63697 20,55 0,18.08136 23.12915,30.44562 40,34 4.43646,0.9347 8.66669,2.66669 13,4 4.33338,1.33332 -8.46619,-4 -13,-4 z"
- id="path4488"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16142);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -492.64377,700.10876 c -3.63409,2.8534 -10.98166,8 -19,8 -6.38199,0 -14.035,-9.89502 -17.00003,-1 -1.14514,3.43549 -7.50299,7.503 -11,11 -5.21661,5.21668 6.8587,8.57605 8,12 2.42413,7.27234 -7.77356,8 -12,8 -8.47662,0 -0.62006,8.79914 1.47968,13 3.28112,6.56434 -8.78467,9.305 -11.47968,12 -5.69196,5.69202 5.54749,13.90509 11,3 3.20606,-6.41198 1.16406,-10 10,-10 2.14899,0 6.99689,5.41535 11,7 4.58502,1.81501 -4.17334,-7.50982 2,-15 7.15064,-8.67596 11.29337,0.65644 13.00003,4 2.85895,5.60108 6.46613,7.4751 6.46613,14 0,12.32038 -13.39908,11 -22.46616,11 -9.0246,0 -14.47583,2.49195 -22,5 -0.14307,0.0477 2.33691,19.19068 2,22 -1.28992,10.75727 2.97205,14.36988 10.47644,20 8.24048,6.18238 18.94824,10.95435 25.99164,18 1.0921,1.09241 2.35464,2 3.53195,3 5.37973,4.56952 8.49634,-20.98541 9,-23 0.4212,-1.68475 0.97571,-3.33331 1.46356,-5 3.78922,-12.94531 -2.60437,-19.00103 -4.46356,-31 -1.64429,-10.61206 3.32968,-21.06012 9,-30 7.17441,-11.31127 8,-0.86047 8,8 0,8.85535 -4.76315,15.23682 2,22 1,1 2,2 3,3 5.06418,5.06415 8.21887,-3.65661 9,-6 2.17905,-6.53717 0.34808,-20.21362 -2.54416,-26 -3.93887,-7.88024 -7.45584,-18 -10.45584,-22 3,-8 -6.16293,-17.65167 -8,-25 -1.56659,-6.26635 -6.15515,-11 -12,-11"
- id="path7756"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16144);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -456.64377,747.10876 c 1.33335,0 1.33335,0 0,0 -1.28406,0 19.59073,0 37,0 10.64774,0 7.74478,7.93451 9.43717,15 2.81787,11.76429 -18.07615,12 -23.99228,12 -7.48163,0 -14.96326,0 -22.44489,0"
- id="path12811"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16146);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -676.6438,755.10876 c -6.16443,0 -20.75122,3 -32,3 -13.70709,0 -11,-2.56237 -11,9 0,9.40528 -15.44958,9 3,9 12.33753,0 23.84741,3 36,3 13.2771,0 9,1.51191 9,-9 0,-7.69043 0.30255,-7.57647 -5,-15 z"
- id="path13627"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:none;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -672.6438,755.10876 c -7.37646,-1.38 -27.20941,-1.40051 -38.41376,0 -5.78485,0.72309 -10.25531,2.5362 -15.98992,3.58625 -4.61518,0.84509 5.72143,12.09601 2.40368,15.41375 -3.85016,3.85022 8.77735,0.47126 15,0.47126 10.9065,0 24.15174,2.52874 35,2.52874 3.03638,0 -2,-10 2,-22 z"
- id="path14439"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16148);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -726.10486,761.10876 c -2.3468,-0.81884 -5.60473,-3.90393 -8.53894,-2 -2.08551,1.35328 -2.01831,10.93226 -1.45788,13 1.68164,6.20472 5.89453,5.56336 9.45788,2 4.64697,-4.64697 1.24158,-12.4649 -2.4602,-15"
- id="path15256"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16150);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -605.6438,870.22467 c -11.46911,-3.29114 -4.21667,0.23932 -15.02514,-5.70026 -15.86664,-7.72992 -24.23328,-22.13055 -34.10999,-35.86193 -7.51269,-11.53266 -12.44525,-24.45093 -15.56335,-37.80414 -4.43726,-19.36566 -4.19275,-39.01642 -0.13868,-58.37769 8.31366,-25.59753 34.62366,-35.97363 57.4004,-46.05926 21.06347,-8.18128 41.60546,-15.77033 64.10797,-18.52893 21.09332,-1.82178 42.47732,2.98333 60.65423,13.80914 13.6099,10.66046 20.48914,28.91498 25.9325,44.91705 7.05945,21.38269 9.42941,43.73932 10.76629,66.10144 2.2472,16.68866 -6.97564,28.14844 -18.49258,38.93732 -22.50113,17.60425 -47.69754,30.26562 -75.21042,37.69623 -16.57282,4.18438 -35.3139,6.53576 -52.32123,4.87103 l 12,-8 c 4.79102,1.09625 -3.18445,-0.8465 1.72119,-0.35248 16.56439,1.59235 33.07147,-0.71295 49.19556,-4.64496 32.13406,-8.34405 24.48367,-5.94879 51.92731,-24.41931 11.324,-10.35956 19.82473,-21.64203 18.81988,-37.73639 -0.49167,-22.34375 -2.90594,-44.70465 -10.15179,-65.96277 -5.82968,-15.90228 -12.84607,-32.77551 -26.2135,-43.72589 -17.63935,-11.07257 -38.56427,-15.74359 -59.29651,-13.57 -22.32886,2.73046 -42.75519,11.00122 -63.75287,18.76611 -19.73889,8.77594 -19.69678,4.01184 -34.65637,31.92401 -4.52491,19.15631 -4.67255,38.41602 0.2597,57.56628 3.42047,13.03925 8.1413,25.91187 15.34821,37.35236 9.75561,13.53863 31.90295,39.33429 47.79919,46.80304 l -11,2 z"
- id="path3671"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -648.60126,717.3949 c 11.03485,3.82714 40.65076,-3.94745 35.2226,14.29852 -17.35877,17.14642 4.24548,20.11078 11.52423,35.41736 -2.06598,9.63458 -12.00433,15.70721 -16.86017,24.28125 1.2572,13.98767 15.82056,16.41583 26.66766,21.48138 10.30951,11.72168 2.63764,28.7544 4.75702,43.16876 1.28821,25.42579 -19.93255,11.76557 -30.28009,2.63123 -10.61322,-7.30383 -17.34961,-15.73279 -21.87909,-27.67786 -0.61596,-13.3999 0.86365,-24.16455 -7.13311,-35.48559 -13.82734,-9.44843 -18.46271,-26.03906 -24.29364,-41.02149 -3.55689,-12.66186 -8.53266,-26.7611 0.1549,-37.83197 l 8.64832,-3.61175 c -9.16718,10.52679 -4.08277,24.63751 -0.77319,37.00079 5.49377,15.27643 10.88238,30.86615 24.7149,40.39685 8.08881,11.69825 6.63965,22.1933 7.30927,35.87592 4.23223,11.82678 11.37512,19.72967 21.28271,27.38129 13.32813,11.74475 19.7605,21.48217 13.91473,6.76971 -2.35406,-14.3797 5.03107,-30.45032 -4.55645,-42.29431 -11.37757,-5.24713 -25.7804,-8.05371 -27.5868,-22.43897 12.24061,-25.43695 20.65247,-1.50244 17.47083,-23.98541 -6.8385,-15.68158 -29.80841,-18.60498 -11.78937,-36.12872 2.66644,-17.0802 -22.71161,-9.59406 -35.15613,-11.9491 l 8.64087,-6.27789 z"
- id="path3676"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -496.14905,699.55633 c -6.32892,15.05341 -22.09826,5.46686 -32.98987,3.55774 0.98523,0.4607 -2.11364,13.20386 -14.43023,15.58521 -3.39191,-2.41809 7.64923,7.40521 12.10711,13.19537 -0.87268,10.34845 -13.05999,7.73609 -20.50488,8.40863 4.88025,-4.5722 3.74133,1.00788 10.45276,5.04889 -0.95593,4.47321 1.1015,7.32929 0.63379,11.52948 -3.00513,3.01728 -27.86292,11.68152 -15.68958,5.99066 0.9068,4.57441 4.28327,6.0506 8.57593,7.62153 9.78491,5.02288 5.65076,3.6408 2.90277,3.77526 -0.002,-5.19934 3.86371,-9.34539 5.82984,-14.19799 9.73602,-10.90009 13.21673,-6.44379 20.40808,0.96087 11.82605,5.03168 1.59405,8.09906 -2.18927,-3.96954 -3.69721,-14.13379 9.685,-10.55359 17.23666,-4.83307 10.07913,11.00018 9.00067,16.81915 -3.69604,23.5069 -6.29056,2.27325 -12.74997,2.45526 -19.24045,1.28711 -5.39777,1.76092 -10.96576,2.14856 -16.67658,1.96563 0.63056,-3.9599 -0.93457,-0.34674 -1.0625,8.99512 6.48786,6.68603 7.18659,11.05048 5.13898,19.97192 0.04,7.05115 5.0589,10.44269 10.23517,14.40699 5.73785,4.15173 12.18658,7.05651 18.36566,10.44586 8.92212,5.63904 13.48306,4.83319 8.71512,6.48688 0.76129,-6.28833 0.82791,-12.09235 3.28787,-18.01032 2.4614,-5.02502 4.75794,-10.35205 6.70148,-15.42816 -1.74835,-5.73456 -3.31455,-11.54309 -5.12305,-17.24634 -1.86493,-7.01483 0.17923,-13.8952 2.62384,-20.5022 1.72904,-9.84686 10.24448,-11.63238 17.93048,-14.30285 1.00534,6.17614 -4.34106,10.92614 -4.57654,17.35498 0.27457,8.72485 3.18097,11.70526 9.9871,15.69329 2.06781,2.39655 4.54849,2.34778 7.45562,2.74274 l -5.90399,4.21436 c -3.00305,-0.53833 -5.63888,-0.65168 -7.71466,-3.20459 -7.00958,-4.02704 -9.43158,-7.59638 -10.03745,-16.22773 0.0347,-6.32574 4.16108,-10.63916 4.6944,-16.58038 -2.56879,1.60907 -4.11706,4.56458 -5.42093,7.3009 -2.39099,6.42066 -4.80816,13.19178 -2.78238,20.00007 1.76117,5.79663 3.21082,11.68273 5.30982,17.40075 -1.76776,5.18634 -4.16675,10.67884 -6.64264,15.88208 -2.61731,5.87781 -2.98731,11.52558 -3.20242,17.93543 -10.95237,8.078 -10.80796,6.41504 -21.13797,0.3128 -6.32825,-3.23004 -12.64484,-6.46069 -18.53113,-10.47735 -5.22119,-4.15192 -10.65674,-7.72773 -10.58997,-15.06385 2.03943,-8.914 1.77698,-13.06335 -5.07397,-19.70025 -0.0463,-9.72028 2.67993,-13.90149 13.13098,-16.50202 5.78576,0.0752 11.23852,-0.81982 16.77502,-1.97577 6.40723,1.09632 12.7591,0.86255 18.91904,-1.51001 -6.08591,3.81269 2.88571,-6.70465 -7.6897,-15.53155 -9.86471,-7.34955 -6.96164,-13.37927 -5.42898,-2.06928 3.19641,10.06592 1.65655,17.8678 -9.80085,10.98475 -7.32592,-7.87958 -7.03851,-10.21955 -7.95611,-7.58545 -1.82544,4.70471 -5.27863,8.74298 -5.93134,13.58264 -4.43225,4.99023 -7.15948,10.11835 -15.2174,3.68274 -4.7652,-1.69812 -8.13611,-3.62 -9.26941,-8.63172 4.22284,-2.96496 25.57592,-12.84594 15.65954,-6.19677 0.37049,-3.58356 -1.08703,-6.63074 -0.43219,-10.83106 -7.30652,-4.25006 -9.00421,-8.97406 1.94464,-12.93036 12.66999,0.0111 12.06226,0.5365 8.51087,-0.53479 -7.08881,-9.47778 -20.03998,-12.20349 -0.54309,-20.80316 5.11267,-1.2688 -8.6214,-2.11798 14.4989,-15.38959 13.13785,3.60566 20.14169,9.1828 20.72491,3.83722 l 6.72919,-3.42865 z"
- id="path6944"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -458.81119,742.42505 c 15.53311,1.36719 31.15543,0.72522 46.72348,0.40423 0.79883,-0.0278 1.59769,-0.0556 2.39655,-0.0834 L -418,748.78265 c -0.72238,-0.0278 -1.44476,-0.0556 -2.16712,-0.0834 -15.94259,-0.34851 -31.92059,-0.88141 -47.83621,0.40424 l 9.19214,-6.67847 z"
- id="path12785"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -452.14667,770.41602 c 15.14096,1.20092 30.3816,-0.24176 45.54053,-0.84082 0.74399,-0.0246 1.48801,-0.0491 2.232,-0.0737 l -8.33884,6.08117 c -0.71033,10e-4 -1.42066,0.002 -2.13098,0.004 -15.5069,0.0591 -31.03095,0.26391 -46.49485,1.50817 l 9.19214,-6.67846 z"
- id="path12787"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -672.6438,757.10876 c -19.19073,-1.41638 -38.75305,-1.61181 -57.97461,-0.0466 l 8.18329,-6.02783 c 19.65021,-0.38421 39.38153,1.6294 58.98346,-0.60406 l -9.19214,6.67846 z"
- id="path12793"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -674.6438,782.26941 c -9.40344,-0.7309 -20.84961,-0.81085 -30.9707,-0.79456 -9.66834,-0.18615 -19.27405,0.0883 -28.90064,0.87946 -0.49682,0.0606 -0.99353,0.12121 -1.49035,0.18182 l 8.60632,-6.36621 c 0.44592,-0.0204 0.89197,-0.0408 1.33795,-0.0612 9.52851,-0.81121 19.05158,-0.81567 28.61658,-0.56353 10.81818,0.14068 22.68817,-0.70435 32.92462,1.56591 l -10.12378,5.15827 z"
- id="path12795"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -411.49161,741.97943 c 8.86563,1.04962 10.70334,9.30969 11.04904,17.12933 -0.13354,5.46955 -2.45565,8.96253 -6.2066,12.48993 l -9.9946,3.88343 c 4.08429,-3.10822 6.70523,-6.28479 7.09946,-11.76093 -0.22785,-7.74536 -2.17697,-15.40033 -11.13944,-15.06323 l 9.19214,-6.67853 z"
- id="path12799"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -720.8338,751.10876 c 5.58087,5.83991 6.40442,12.33826 2.9151,19.49067 -0.68451,1.14819 -1.82025,1.89264 -2.7251,2.83349 l -9.94958,4.05017 c 0.94378,-0.86071 1.96435,-1.64056 2.83252,-2.58313 3.80145,-6.32824 3.96905,-12.67089 -2.26508,-17.11273 l 9.19214,-6.67847 z"
- id="path12803"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -734.1073,782.78723 c -8.92578,-5.52331 -11.22583,-12.61609 -5.11804,-21.36267 l 9.46869,-4.01562 c -6.10077,7.70562 -4.95514,15.39373 4.84143,18.69982 l -9.19208,6.67847 z"
- id="path12809"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:#000000;fill-rule:nonzero;stroke-width:4.71999979;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -596.6438,769.38434 c 3.85455,-3.09064 14.72229,-16 1,-16 -6.02246,0 11.63819,-12.15955 -9,-7 -1.87292,0.4682 -3.62366,1.33331 -5.43561,2 -13.2763,4.88495 -13.72332,5.39032 -24.56439,11 -1.48022,0.76593 2.66669,2 4,3 1.31323,0.98492 -52.62549,14.88879 -24,9.4928 5.2638,-0.99219 10.66669,-0.99518 16,-1.4928 3.87055,-0.36115 -9.47693,8.26153 -6,10 3.64191,1.82092 -12.94421,6.80169 2.51923,4 32.22284,-5.83832 -8.00043,3.1203 24.48077,-5 1.33331,-0.33338 2.66669,-0.66669 4,-1 6.19922,-1.54981 19.00781,-0.969 17,-9 z"
- id="path8573"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16152);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -526.6438,844.10876 c -0.23395,0.78241 -21.3327,1.53309 -25,3 -11.2738,4.50953 -25.284,-16.86401 -31,6 -2.33594,9.34382 -19.55908,-6.76361 -18,-13 4.58069,-18.32251 11.58838,8.97095 2,-15 -5.03326,-12.58313 23.36273,-7.36047 25,-11 0.27887,-0.61981 31.72803,-11.25164 11,6 -1.37964,1.14832 -1.33331,3.33332 -2,5 -2.15387,5.38483 17.82373,-14.97064 30,-17 0.16809,-0.028 -16.78216,15.31775 -1,8.56061 0.70722,-0.30273 -18.17944,12.87531 -1,9.43939 24.19104,-4.83819 33.24146,-6.4757 24,8.46827 -0.17096,0.27655 18.75858,-8.22785 4.00003,4.53173 -4.87735,4.21674 -12.10525,3.69147 -18.00003,5 z"
- id="path10193"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16154);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- <path
- d="m -628.6438,728.10876 c 7.37012,0 4.37189,-14.63665 9.42688,-20 6.63281,-7.03747 15.72028,-10.7868 24.57312,-13 7.53095,-1.88275 12.49451,-6 21,-6 5.80225,0 12.73548,-0.0662 17,1 2.61798,0.65448 -12.14123,7.08197 -15.44787,9 -7.33143,4.25269 -17.38038,5.73133 -25.55213,9 -1.82599,0.73041 -3.70032,1.33332 -5.55054,2 -8.80194,3.17151 -16.22821,8.763 -24.44946,12 -3.3819,1.33155 -1.63086,3.47638 -1,6 z"
- id="path11021"
- stroke-miterlimit="4"
- inkscape:connector-curvature="0"
- style="fill:url(#linearGradient16156);fill-rule:evenodd;stroke-width:4.7235918;stroke-linecap:round;stroke-miterlimit:4;stroke-dashoffset:0;marker-start:none;marker-mid:none;marker-end:none" />
- </g>
- </g>
- </g>
- <text
- xml:space="preserve"
- style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
- x="240.24515"
- y="352.26129"
- id="text8890"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- id="tspan8892"
- x="240.24515"
- y="352.26129"
- style="font-size:56px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans">CloudInit</tspan></text>
- </g>
-</svg>
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="artwork" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="1190.551px" height="841.89px" viewBox="0 0 1190.551 841.89" enable-background="new 0 0 1190.551 841.89"
+ xml:space="preserve">
+<g>
+ <g>
+ <path fill="#000020" d="M219.086,722.686c-9.061,0-17.009-1.439-23.838-4.314c-6.833-2.875-12.586-6.902-17.258-12.082
+ c-4.675-5.176-8.164-11.324-10.463-18.443c-2.302-7.119-3.452-14.992-3.452-23.623c0-8.629,1.257-16.537,3.775-23.73
+ c2.515-7.189,6.074-13.408,10.679-18.658c4.601-5.25,10.247-9.348,16.935-12.299c6.688-2.945,14.13-4.422,22.328-4.422
+ c5.033,0,10.065,0.432,15.101,1.295c5.033,0.863,9.85,2.232,14.454,4.1l-4.53,17.041c-3.021-1.436-6.509-2.588-10.463-3.451
+ c-3.957-0.863-8.164-1.295-12.62-1.295c-11.218,0-19.813,3.527-25.779,10.572c-5.97,7.049-8.953,17.332-8.953,30.848
+ c0,6.041,0.681,11.58,2.05,16.611c1.365,5.037,3.522,9.35,6.472,12.945c2.946,3.596,6.721,6.361,11.326,8.305
+ c4.601,1.941,10.21,2.912,16.827,2.912c5.319,0,10.139-0.502,14.454-1.51c4.314-1.006,7.692-2.084,10.139-3.236l2.804,16.826
+ c-1.153,0.723-2.804,1.402-4.962,2.051c-2.157,0.646-4.604,1.219-7.334,1.725c-2.734,0.502-5.646,0.934-8.737,1.295
+ C224.944,722.504,221.961,722.686,219.086,722.686z"/>
+ <path fill="#000020" d="M298.343,722.254c-12.371-0.289-21.141-2.945-26.319-7.982c-5.178-5.031-7.766-12.869-7.766-23.514
+ V556.143l20.062-3.451v134.83c0,3.311,0.287,6.041,0.863,8.197c0.573,2.158,1.51,3.885,2.805,5.178
+ c1.294,1.295,3.02,2.266,5.177,2.914c2.158,0.646,4.817,1.186,7.982,1.617L298.343,722.254z"/>
+ <path fill="#000020" d="M415.288,664.008c0,8.92-1.294,16.971-3.883,24.162c-2.589,7.193-6.223,13.375-10.895,18.553
+ c-4.675,5.176-10.247,9.168-16.72,11.971c-6.471,2.807-13.52,4.207-21.141,4.207c-7.624,0-14.669-1.4-21.141-4.207
+ c-6.472-2.803-12.047-6.795-16.72-11.971c-4.675-5.178-8.305-11.359-10.894-18.553c-2.588-7.191-3.883-15.242-3.883-24.162
+ c0-8.771,1.294-16.789,3.883-24.055c2.589-7.26,6.219-13.482,10.894-18.66c4.673-5.178,10.248-9.168,16.72-11.973
+ c6.472-2.805,13.517-4.207,21.141-4.207c7.621,0,14.67,1.402,21.141,4.207c6.473,2.805,12.045,6.795,16.72,11.973
+ c4.672,5.178,8.306,11.4,10.895,18.66C413.994,647.219,415.288,655.236,415.288,664.008z M394.362,664.008
+ c0-12.654-2.841-22.686-8.521-30.094c-5.684-7.406-13.412-11.111-23.191-11.111c-9.781,0-17.512,3.705-23.19,11.111
+ c-5.683,7.408-8.521,17.439-8.521,30.094c0,12.656,2.838,22.688,8.521,30.094c5.679,7.41,13.409,11.109,23.19,11.109
+ c9.779,0,17.508-3.699,23.191-11.109C391.521,686.695,394.362,676.664,394.362,664.008z"/>
+ <path fill="#000020" d="M527.186,716.861c-4.604,1.152-10.678,2.373-18.229,3.666c-7.551,1.295-16.288,1.943-26.211,1.943
+ c-8.629,0-15.893-1.262-21.789-3.777c-5.898-2.514-10.645-6.072-14.238-10.678c-3.596-4.602-6.184-10.031-7.766-16.287
+ c-1.584-6.256-2.373-13.193-2.373-20.818v-62.992h20.062v58.678c0,13.666,2.158,23.443,6.472,29.34
+ c4.315,5.898,11.575,8.844,21.789,8.844c2.157,0,4.386-0.07,6.688-0.215c2.299-0.141,4.455-0.324,6.471-0.539
+ c2.012-0.215,3.846-0.432,5.501-0.648c1.652-0.215,2.839-0.465,3.56-0.754v-94.705h20.062V716.861z"/>
+ <path fill="#000020" d="M628.963,556.143l20.064-3.451v164.17c-4.605,1.293-10.502,2.588-17.691,3.883
+ c-7.193,1.295-15.461,1.941-24.809,1.941c-8.629,0-16.396-1.369-23.298-4.098c-6.903-2.732-12.802-6.615-17.69-11.65
+ c-4.891-5.033-8.666-11.182-11.326-18.445c-2.662-7.26-3.99-15.424-3.99-24.484c0-8.629,1.113-16.537,3.344-23.73
+ c2.229-7.189,5.502-13.375,9.816-18.553c4.313-5.178,9.6-9.201,15.855-12.08c6.256-2.875,13.409-4.314,21.465-4.314
+ c6.471,0,12.188,0.863,17.15,2.588c4.961,1.727,8.662,3.381,11.109,4.963V556.143z M628.963,631.648
+ c-2.447-2.014-5.969-3.953-10.57-5.824c-4.604-1.867-9.637-2.805-15.102-2.805c-5.754,0-10.678,1.045-14.776,3.127
+ c-4.1,2.088-7.443,4.963-10.033,8.631c-2.588,3.666-4.459,8.018-5.607,13.051c-1.153,5.035-1.727,10.43-1.727,16.18
+ c0,13.088,3.236,23.189,9.708,30.311c6.472,7.119,15.101,10.678,25.888,10.678c5.463,0,10.031-0.25,13.697-0.756
+ c3.668-0.502,6.506-1.041,8.521-1.617V631.648z"/>
+ <path fill="#000020" d="M671.375,646.102h53.283v18.77h-53.283V646.102z"/>
+ <path fill="#000020" d="M755.745,587.641c-3.598,0-6.654-1.188-9.168-3.561c-2.52-2.373-3.777-5.57-3.777-9.6
+ c0-4.025,1.258-7.227,3.777-9.6c2.514-2.373,5.57-3.559,9.168-3.559c3.592,0,6.65,1.186,9.168,3.559
+ c2.516,2.373,3.775,5.574,3.775,9.6c0,4.029-1.26,7.227-3.775,9.6C762.395,586.453,759.336,587.641,755.745,587.641z
+ M765.883,720.098h-20.062v-112.18h20.062V720.098z"/>
+ <path fill="#000020" d="M794.401,611.154c4.602-1.148,10.713-2.373,18.336-3.668c7.621-1.293,16.396-1.941,26.32-1.941
+ c8.914,0,16.32,1.262,22.219,3.775c5.896,2.518,10.605,6.041,14.131,10.57c3.523,4.531,6.002,9.961,7.441,16.287
+ c1.438,6.332,2.158,13.305,2.158,20.926v62.994h-20.062v-58.68c0-6.902-0.469-12.797-1.402-17.689
+ c-0.938-4.887-2.48-8.844-4.639-11.863c-2.156-3.021-5.035-5.213-8.629-6.58c-3.596-1.365-8.055-2.051-13.375-2.051
+ c-2.156,0-4.389,0.074-6.688,0.217c-2.303,0.145-4.496,0.322-6.58,0.539c-2.086,0.215-3.957,0.469-5.609,0.756
+ c-1.654,0.289-2.84,0.504-3.559,0.646v94.705h-20.062V611.154z"/>
+ <path fill="#000020" d="M922.088,587.641c-3.598,0-6.654-1.188-9.168-3.561c-2.52-2.373-3.777-5.57-3.777-9.6
+ c0-4.025,1.258-7.227,3.777-9.6c2.514-2.373,5.57-3.559,9.168-3.559c3.592,0,6.65,1.186,9.168,3.559
+ c2.516,2.373,3.775,5.574,3.775,9.6c0,4.029-1.26,7.227-3.775,9.6C928.739,586.453,925.68,587.641,922.088,587.641z
+ M932.227,720.098h-20.062v-112.18h20.062V720.098z"/>
+ <path fill="#000020" d="M979.663,607.918h42.5v16.828h-42.5v51.773c0,5.609,0.432,10.248,1.295,13.914
+ c0.863,3.668,2.158,6.547,3.883,8.631c1.727,2.086,3.883,3.559,6.473,4.422c2.588,0.863,5.607,1.293,9.061,1.293
+ c6.041,0,10.895-0.68,14.561-2.049c3.668-1.365,6.221-2.336,7.658-2.912l3.885,16.611c-2.016,1.008-5.539,2.264-10.57,3.775
+ c-5.037,1.51-10.787,2.266-17.26,2.266c-7.625,0-13.914-0.971-18.877-2.914c-4.961-1.941-8.951-4.854-11.971-8.736
+ c-3.021-3.883-5.145-8.662-6.365-14.346c-1.223-5.68-1.834-12.26-1.834-19.738V576.639l20.062-3.453V607.918z"/>
+ </g>
+ <g>
+ <path fill="#E95420" d="M595.275,150.171c-93.932,0-170.078,76.146-170.078,170.079c0,53.984,25.157,102.088,64.381,133.245
+ v-37.423c0-8.807,7.137-15.943,15.943-15.943s15.947,7.137,15.947,15.943v57.45c3.478,1.678,7.024,3.233,10.629,4.677v-62.127
+ c0-8.807,7.139-15.943,15.943-15.943c8.807,0,15.945,7.137,15.945,15.943v71.374c3.508,0.652,7.053,1.198,10.633,1.631v-73.005
+ c0-8.807,7.139-15.943,15.945-15.943c8.805,0,15.944,7.137,15.944,15.943v73.878c3.572-0.232,7.119-0.565,10.629-1.016V352.287
+ c0-8.801,7.137-15.943,15.943-15.943s15.943,7.143,15.943,15.943v129.365c67.59-22.497,116.33-86.255,116.33-161.402
+ C765.354,226.317,689.208,150.171,595.275,150.171z"/>
+ <path fill="#FFFFFF" d="M696.856,320.25H569.303c-21.133,0-38.27-17.125-38.27-38.27c0-20.339,15.871-36.965,35.898-38.192
+ c-8.953-17.953-2.489-40.012,15.128-50.188c17.611-10.165,39.949-4.739,51.019,11.997c11.076-16.736,33.416-22.162,51.025-11.994
+ c17.621,10.173,24.08,32.226,15.119,50.185c20.037,1.228,35.906,17.854,35.906,38.192
+ C735.129,303.125,717.993,320.25,696.856,320.25z"/>
+ <g>
+ <path fill="#E95420" d="M633.014,271.05c4.074,0,7.375-3.302,7.375-7.371v-34.547c0-4.07-3.301-7.37-7.375-7.37
+ c-4.068,0-7.369,3.3-7.369,7.37v34.547C625.645,267.748,628.946,271.05,633.014,271.05z"/>
+ <path fill="#E95420" d="M650.254,238.483c-3.043,2.7-3.316,7.36-0.615,10.405c7.746,8.728,7.348,22.036-0.916,30.302
+ c-4.174,4.176-9.729,6.476-15.639,6.476c-5.9,0-11.457-2.303-15.637-6.479c-8.291-8.291-8.67-21.625-0.863-30.356
+ c2.715-3.034,2.455-7.695-0.578-10.409c-3.035-2.712-7.699-2.453-10.41,0.582c-13.02,14.556-12.391,36.787,1.43,50.607
+ c6.961,6.963,16.211,10.797,26.059,10.797c0,0,0,0,0.002,0c9.846,0,19.098-3.831,26.064-10.793
+ c13.77-13.776,14.432-35.96,1.512-50.514C657.961,236.055,653.299,235.777,650.254,238.483z"/>
+ <path fill="#E95420" d="M632.788,344.26c-4.4,0-7.969,3.568-7.969,7.969c0,4.406,3.568,7.975,7.969,7.975
+ c4.406,0,7.975-3.568,7.975-7.975C640.762,347.828,637.194,344.26,632.788,344.26z"/>
+ </g>
+ </g>
+</g>
+</svg>
diff --git a/doc/rtd/topics/datasources.rst b/doc/rtd/topics/datasources.rst
index cc0d0ede..0d7d4aca 100644
--- a/doc/rtd/topics/datasources.rst
+++ b/doc/rtd/topics/datasources.rst
@@ -166,7 +166,7 @@ For now see: http://maas.ubuntu.com/
CloudStack
---------------------------
-*TODO*
+.. include:: ../../sources/cloudstack/README.rst
---------------------------
OVF
@@ -177,6 +177,12 @@ OVF
For now see: https://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/files/head:/doc/sources/ovf/
---------------------------
+OpenStack
+---------------------------
+
+.. include:: ../../sources/openstack/README.rst
+
+---------------------------
Fallback/None
---------------------------
diff --git a/doc/sources/cloudstack/README.rst b/doc/sources/cloudstack/README.rst
new file mode 100644
index 00000000..eba1cd7e
--- /dev/null
+++ b/doc/sources/cloudstack/README.rst
@@ -0,0 +1,29 @@
+`Apache CloudStack`_ expose user-data, meta-data, user password and account
+sshkey thru the Virtual-Router. For more details on meta-data and user-data,
+refer the `CloudStack Administrator Guide`_.
+
+URLs to access user-data and meta-data from the Virtual Machine. Here 10.1.1.1
+is the Virtual Router IP:
+
+.. code:: bash
+
+ http://10.1.1.1/latest/user-data
+ http://10.1.1.1/latest/meta-data
+ http://10.1.1.1/latest/meta-data/{metadata type}
+
+Configuration
+~~~~~~~~~~~~~
+
+Apache CloudStack datasource can be configured as follows:
+
+.. code:: yaml
+
+ datasource:
+ CloudStack: {}
+ None: {}
+ datasource_list:
+ - CloudStack
+
+
+.. _Apache CloudStack: http://cloudstack.apache.org/
+.. _CloudStack Administrator Guide: http://docs.cloudstack.apache.org/projects/cloudstack-administration/en/latest/virtual_machines.html#user-data-and-meta-data \ No newline at end of file
diff --git a/doc/sources/nocloud/README.rst b/doc/sources/nocloud/README.rst
index aa3cf1a3..08a39377 100644
--- a/doc/sources/nocloud/README.rst
+++ b/doc/sources/nocloud/README.rst
@@ -3,7 +3,7 @@ and meta-data to the instance without running a network service (or even without
having a network at all).
You can provide meta-data and user-data to a local vm boot via files on a `vfat`_
-or `iso9660`_ filesystem.
+or `iso9660`_ filesystem. The filesystem volume label must be ``cidata``.
These user-data and meta-data files are expected to be
in the following format.
diff --git a/doc/sources/openstack/README.rst b/doc/sources/openstack/README.rst
new file mode 100644
index 00000000..8102597e
--- /dev/null
+++ b/doc/sources/openstack/README.rst
@@ -0,0 +1,24 @@
+*TODO*
+
+Vendor Data
+~~~~~~~~~~~
+
+The OpenStack metadata server can be configured to serve up vendor data
+which is available to all instances for consumption. OpenStack vendor
+data is, generally, a JSON object.
+
+cloud-init will look for configuration in the ``cloud-init`` attribute
+of the vendor data JSON object. cloud-init processes this configuration
+using the same handlers as user data, so any formats that work for user
+data should work for vendor data.
+
+For example, configuring the following as vendor data in OpenStack would
+upgrade packages and install ``htop`` on all instances:
+
+.. sourcecode:: json
+
+ {"cloud-init": "#cloud-config\npackage_upgrade: True\npackages:\n - htop"}
+
+For more general information about how cloud-init handles vendor data,
+including how it can be disabled by users on instances, see
+https://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/view/head:/doc/vendordata.txt
diff --git a/packages/bddeb b/packages/bddeb
index 9d264f92..c141b1ab 100755
--- a/packages/bddeb
+++ b/packages/bddeb
@@ -1,5 +1,6 @@
-#!/usr/bin/python
+#!/usr/bin/env python3
+import glob
import os
import shutil
import sys
@@ -27,22 +28,35 @@ import argparse
# Package names that will showup in requires to what we can actually
# use in our debian 'control' file, this is a translation of the 'requires'
# file pypi package name to a debian/ubuntu package name.
-PKG_MP = {
- 'argparse': 'python-argparse',
- 'cheetah': 'python-cheetah',
- 'configobj': 'python-configobj',
- 'jinja2': 'python-jinja2',
- 'jsonpatch': 'python-jsonpatch | python-json-patch',
- 'oauth': 'python-oauth',
- 'prettytable': 'python-prettytable',
- 'pyserial': 'python-serial',
- 'pyyaml': 'python-yaml',
- 'requests': 'python-requests',
+STD_NAMED_PACKAGES = [
+ 'configobj',
+ 'jinja2',
+ 'jsonpatch',
+ 'oauthlib',
+ 'prettytable',
+ 'requests',
+ 'six',
+ 'httpretty',
+ 'mock',
+ 'nose',
+ 'setuptools',
+]
+NONSTD_NAMED_PACKAGES = {
+ 'argparse': ('python-argparse', None),
+ 'contextlib2': ('python-contextlib2', None),
+ 'cheetah': ('python-cheetah', None),
+ 'pyserial': ('python-serial', 'python3-serial'),
+ 'pyyaml': ('python-yaml', 'python3-yaml'),
+ 'six': ('python-six', 'python3-six'),
+ 'pep8': ('pep8', 'python3-pep8'),
+ 'pyflakes': ('pyflakes', 'pyflakes'),
}
+
DEBUILD_ARGS = ["-S", "-d"]
-def write_debian_folder(root, version, revno, append_requires=[]):
+def write_debian_folder(root, version, revno, pkgmap,
+ pyver="3", append_requires=[]):
deb_dir = util.abs_join(root, 'debian')
os.makedirs(deb_dir)
@@ -58,28 +72,45 @@ def write_debian_folder(root, version, revno, append_requires=[]):
# Write out the control file template
cmd = [util.abs_join(find_root(), 'tools', 'read-dependencies')]
(stdout, _stderr) = util.subp(cmd)
- pkgs = [p.lower().strip() for p in stdout.splitlines()]
+ pypi_pkgs = [p.lower().strip() for p in stdout.splitlines()]
+
+ (stdout, _stderr) = util.subp(cmd + ['test-requirements.txt'])
+ pypi_test_pkgs = [p.lower().strip() for p in stdout.splitlines()]
# Map to known packages
requires = append_requires
- for p in pkgs:
- tgt_pkg = PKG_MP.get(p)
- if not tgt_pkg:
- raise RuntimeError(("Do not know how to translate pypi dependency"
- " %r to a known package") % (p))
- else:
- requires.append(tgt_pkg)
+ test_requires = []
+ lists = ((pypi_pkgs, requires), (pypi_test_pkgs, test_requires))
+ for pypilist, target in lists:
+ for p in pypilist:
+ if p not in pkgmap:
+ raise RuntimeError(("Do not know how to translate pypi "
+ "dependency %r to a known package") % (p))
+ elif pkgmap[p]:
+ target.append(pkgmap[p])
+
+ if pyver == "3":
+ python = "python3"
+ else:
+ python = "python"
templater.render_to_file(util.abs_join(find_root(),
'packages', 'debian', 'control.in'),
util.abs_join(deb_dir, 'control'),
- params={'requires': requires})
+ params={'requires': ','.join(requires),
+ 'test_requires': ','.join(test_requires),
+ 'python': python})
+
+ templater.render_to_file(util.abs_join(find_root(),
+ 'packages', 'debian', 'rules.in'),
+ util.abs_join(deb_dir, 'rules'),
+ params={'python': python, 'pyver': pyver})
- # Just copy the following directly
- for base_fn in ['dirs', 'copyright', 'compat', 'rules']:
- shutil.copy(util.abs_join(find_root(),
- 'packages', 'debian', base_fn),
- util.abs_join(deb_dir, base_fn))
+ # Just copy any other files directly (including .in)
+ pdeb_d = util.abs_join(find_root(), 'packages', 'debian')
+ for f in [os.path.join(pdeb_d, f) for f in os.listdir(pdeb_d)]:
+ if os.path.isfile(f):
+ shutil.copy(f, util.abs_join(deb_dir, os.path.basename(f)))
def main():
@@ -90,12 +121,16 @@ def main():
" (default: %(default)s)"),
default=False,
action='store_true')
- parser.add_argument("--no-cloud-utils", dest="no_cloud_utils",
- help=("don't depend on cloud-utils package"
+ parser.add_argument("--cloud-utils", dest="cloud_utils",
+ help=("depend on cloud-utils package"
" (default: %(default)s)"),
default=False,
action='store_true')
+ parser.add_argument("--python2", dest="python2",
+ help=("build debs for python2 rather than python3"),
+ default=False, action='store_true')
+
parser.add_argument("--init-system", dest="init_system",
help=("build deb with INIT_SYSTEM=xxx"
" (default: %(default)s"),
@@ -122,6 +157,18 @@ def main():
if args.verbose:
capture = False
+ pkgmap = {}
+ for p in NONSTD_NAMED_PACKAGES:
+ pkgmap[p] = NONSTD_NAMED_PACKAGES[p][int(not args.python2)]
+
+ for p in STD_NAMED_PACKAGES:
+ if args.python2:
+ pkgmap[p] = "python-" + p
+ pyver = "2"
+ else:
+ pkgmap[p] = "python3-" + p
+ pyver = "3"
+
with util.tempdir() as tdir:
cmd = [util.abs_join(find_root(), 'tools', 'read-version')]
@@ -152,11 +199,12 @@ def main():
shutil.move(extracted_name, xdir)
print("Creating a debian/ folder in %r" % (xdir))
- if not args.no_cloud_utils:
+ if args.cloud_utils:
append_requires=['cloud-utils | cloud-guest-utils']
else:
append_requires=[]
- write_debian_folder(xdir, version, revno, append_requires)
+ write_debian_folder(xdir, version, revno, pkgmap,
+ pyver=pyver, append_requires=append_requires)
# The naming here seems to follow some debian standard
# so it will whine if it is changed...
diff --git a/packages/brpm b/packages/brpm
index 9657b1dd..b41b675f 100755
--- a/packages/brpm
+++ b/packages/brpm
@@ -40,22 +40,24 @@ PKG_MP = {
'jinja2': 'python-jinja2',
'configobj': 'python-configobj',
'jsonpatch': 'python-jsonpatch',
- 'oauth': 'python-oauth',
+ 'oauthlib': 'python-oauthlib',
'prettytable': 'python-prettytable',
'pyserial': 'pyserial',
'pyyaml': 'PyYAML',
'requests': 'python-requests',
+ 'six': 'python-six',
},
'suse': {
'argparse': 'python-argparse',
'cheetah': 'python-cheetah',
'configobj': 'python-configobj',
'jsonpatch': 'python-jsonpatch',
- 'oauth': 'python-oauth',
+ 'oauthlib': 'python-oauthlib',
'prettytable': 'python-prettytable',
'pyserial': 'python-pyserial',
'pyyaml': 'python-yaml',
'requests': 'python-requests',
+ 'six': 'python-six',
}
}
diff --git a/packages/debian/changelog.in b/packages/debian/changelog.in
index e3e94f54..c9affe47 100644
--- a/packages/debian/changelog.in
+++ b/packages/debian/changelog.in
@@ -1,4 +1,4 @@
-## This is a cheetah template
+## template:basic
cloud-init (${version}~bzr${revision}-1) UNRELEASED; urgency=low
* build
diff --git a/packages/debian/cloud-init.postinst b/packages/debian/cloud-init.postinst
new file mode 100644
index 00000000..cdd0466d
--- /dev/null
+++ b/packages/debian/cloud-init.postinst
@@ -0,0 +1,16 @@
+#!/bin/sh
+cleanup_lp1552999() {
+ local oldver="$1" last_bad_ver="0.7.7~bzr1178"
+ dpkg --compare-versions "$oldver" le "$last_bad_ver" || return 0
+ local edir="/etc/systemd/system/multi-user.target.wants"
+ rm -f "$edir/cloud-config.service" "$edir/cloud-final.service" \
+ "$edir/cloud-init-local.service" "$edir/cloud-init.service"
+}
+
+
+#DEBHELPER#
+
+if [ "$1" = "configure" ]; then
+ oldver="$2"
+ cleanup_lp1552999 "$oldver"
+fi
diff --git a/packages/debian/cloud-init.preinst b/packages/debian/cloud-init.preinst
new file mode 100644
index 00000000..3c2af06d
--- /dev/null
+++ b/packages/debian/cloud-init.preinst
@@ -0,0 +1,20 @@
+#!/bin/sh
+# vi: ts=4 expandtab
+
+cleanup_lp1552999() {
+ local oldver="$1" last_bad_ver="0.7.7~bzr1178"
+ dpkg --compare-versions "$oldver" le "$last_bad_ver" || return 0
+ local hdir="/var/lib/systemd/deb-systemd-helper-enabled"
+ hdir="$hdir/multi-user.target.wants"
+ local edir="/etc/systemd/system/multi-user.target.wants"
+ rm -f "$hdir/cloud-config.service" "$hdir/cloud-final.service" \
+ "$hdir/cloud-init-local.service" "$hdir/cloud-init.service"
+}
+
+
+if [ "$1" = "upgrade" ]; then
+ oldver="$2"
+ cleanup_lp1552999 "$oldver"
+fi
+
+#DEBHELPER#
diff --git a/packages/debian/control.in b/packages/debian/control.in
index 9207e5f4..b58561e7 100644
--- a/packages/debian/control.in
+++ b/packages/debian/control.in
@@ -1,4 +1,4 @@
-## This is a cheetah template
+## template:basic
Source: cloud-init
Section: admin
Priority: optional
@@ -6,31 +6,24 @@ Maintainer: Scott Moser <smoser@ubuntu.com>
Build-Depends: debhelper (>= 9),
dh-python,
dh-systemd,
- python (>= 2.6.6-3~),
- python-nose,
+ iproute2,
+ pep8,
pyflakes,
- python-setuptools,
- python-selinux,
- python-cheetah,
- python-mocker,
- python-httpretty,
-#for $r in $requires
- ${r},
-#end for
+ python3-pyflakes | pyflakes (<< 1.1.0-2),
+ ${python},
+ ${test_requires},
+ ${requires}
XS-Python-Version: all
-Standards-Version: 3.9.3
+Standards-Version: 3.9.6
Package: cloud-init
Architecture: all
Depends: procps,
- python,
-#for $r in $requires
- ${r},
-#end for
- python-software-properties | software-properties-common,
- \${misc:Depends},
-Recommends: sudo
-XB-Python-Version: \${python:Versions}
+ ${python},
+ ${misc:Depends},
+ ${${python}:Depends}
+Recommends: eatmydata, sudo, software-properties-common, gdisk
+XB-Python-Version: ${python:Versions}
Description: Init scripts for cloud instances
Cloud instances need special scripts to run during initialisation
to retrieve and install ssh keys and to let the user run various scripts.
diff --git a/packages/debian/copyright b/packages/debian/copyright
index f55bb7a3..c694f30d 100644
--- a/packages/debian/copyright
+++ b/packages/debian/copyright
@@ -5,7 +5,7 @@ Source: https://launchpad.net/cloud-init
This package was debianized by Soren Hansen <soren@ubuntu.com> on
Thu, 04 Sep 2008 12:49:15 +0200 as ec2-init. It was later renamed to
-cloud-utils by Scott Moser <scott.moser@canonical.com>
+cloud-init by Scott Moser <scott.moser@canonical.com>
Upstream Author: Scott Moser <smoser@canonical.com>
Soren Hansen <soren@canonical.com>
diff --git a/packages/debian/dirs b/packages/debian/dirs
index f3de468d..9a633c60 100644
--- a/packages/debian/dirs
+++ b/packages/debian/dirs
@@ -3,3 +3,4 @@ usr/bin
etc/init
usr/share/doc/cloud
etc/cloud
+lib/udev/rules.d
diff --git a/packages/debian/rules b/packages/debian/rules
deleted file mode 100755
index 9e0c5ddb..00000000
--- a/packages/debian/rules
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/make -f
-
-INIT_SYSTEM ?= upstart,systemd
-export PYBUILD_INSTALL_ARGS=--init-system=$(INIT_SYSTEM)
-
-%:
- dh $@ --with python2,systemd --buildsystem pybuild
-
-override_dh_install:
- dh_install
- install -d debian/cloud-init/etc/rsyslog.d
- cp tools/21-cloudinit.conf debian/cloud-init/etc/rsyslog.d/21-cloudinit.conf
-
-override_dh_auto_test:
- # Becuase setup tools didn't copy data...
- cp -r tests/data .pybuild/pythonX.Y_2.7/build/tests
- http_proxy= dh_auto_test -- --test-nose
diff --git a/packages/debian/rules.in b/packages/debian/rules.in
new file mode 100755
index 00000000..cf2dd405
--- /dev/null
+++ b/packages/debian/rules.in
@@ -0,0 +1,23 @@
+## template:basic
+#!/usr/bin/make -f
+INIT_SYSTEM ?= upstart,systemd
+export PYBUILD_INSTALL_ARGS=--init-system=$(INIT_SYSTEM)
+PYVER ?= python${pyver}
+
+%:
+ dh $@ --with $(PYVER),systemd --buildsystem pybuild
+
+override_dh_install:
+ dh_install
+ install -d debian/cloud-init/etc/rsyslog.d
+ cp tools/21-cloudinit.conf debian/cloud-init/etc/rsyslog.d/21-cloudinit.conf
+
+override_dh_auto_test:
+ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS)))
+ http_proxy= make PYVER=${pyver} check
+else
+ @echo check disabled by DEB_BUILD_OPTIONS=$(DEB_BUILD_OPTIONS)
+endif
+
+override_dh_systemd_start:
+ dh_systemd_start --no-restart-on-upgrade --no-start
diff --git a/requirements.txt b/requirements.txt
index 943dbef7..19c88857 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,6 @@
# Pypi requirements for cloud-init to work
# Used for untemplating any files or strings with parameters.
-cheetah
jinja2
# This is used for any pretty printing of tabular data.
@@ -9,7 +8,7 @@ PrettyTable
# This one is currently only used by the MAAS datasource. If that
# datasource is removed, this is no longer needed
-oauth
+oauthlib
# This one is currently used only by the CloudSigma and SmartOS datasources.
# If these datasources are removed, this is no longer needed
@@ -32,3 +31,6 @@ requests
# For patching pieces of cloud-config together
jsonpatch
+
+# For Python 2/3 compatibility
+six
diff --git a/setup.py b/setup.py
index 25f09e58..f86727b2 100755
--- a/setup.py
+++ b/setup.py
@@ -45,44 +45,57 @@ def tiny_p(cmd, capture=True):
stdout = None
stderr = None
sp = subprocess.Popen(cmd, stdout=stdout,
- stderr=stderr, stdin=None)
+ stderr=stderr, stdin=None,
+ universal_newlines=True)
(out, err) = sp.communicate()
ret = sp.returncode
if ret not in [0]:
- raise RuntimeError("Failed running %s [rc=%s] (%s, %s)"
- % (cmd, ret, out, err))
+ raise RuntimeError("Failed running %s [rc=%s] (%s, %s)" %
+ (cmd, ret, out, err))
return (out, err)
-def systemd_unitdir():
- cmd = ['pkg-config', '--variable=systemdsystemunitdir', 'systemd']
+def pkg_config_read(library, var):
+ fallbacks = {
+ 'systemd': {
+ 'systemdsystemunitdir': '/lib/systemd/system',
+ 'systemdsystemgeneratordir': '/lib/systemd/system-generators',
+ }
+ }
+ cmd = ['pkg-config', '--variable=%s' % var, library]
try:
(path, err) = tiny_p(cmd)
except:
- return '/lib/systemd/system'
+ return fallbacks[library][var]
return str(path).strip()
+
INITSYS_FILES = {
'sysvinit': [f for f in glob('sysvinit/redhat/*') if is_f(f)],
'sysvinit_freebsd': [f for f in glob('sysvinit/freebsd/*') if is_f(f)],
'sysvinit_deb': [f for f in glob('sysvinit/debian/*') if is_f(f)],
- 'systemd': [f for f in glob('systemd/*') if is_f(f)],
+ 'systemd': [f for f in (glob('systemd/*.service') +
+ glob('systemd/*.target')) if is_f(f)],
+ 'systemd.generators': [f for f in glob('systemd/*-generator') if is_f(f)],
'upstart': [f for f in glob('upstart/*') if is_f(f)],
}
INITSYS_ROOTS = {
'sysvinit': '/etc/rc.d/init.d',
'sysvinit_freebsd': '/usr/local/etc/rc.d',
'sysvinit_deb': '/etc/init.d',
- 'systemd': systemd_unitdir(),
+ 'systemd': pkg_config_read('systemd', 'systemdsystemunitdir'),
+ 'systemd.generators': pkg_config_read('systemd',
+ 'systemdsystemgeneratordir'),
'upstart': '/etc/init/',
}
-INITSYS_TYPES = sorted(list(INITSYS_ROOTS.keys()))
+INITSYS_TYPES = sorted([f.partition(".")[0] for f in INITSYS_ROOTS.keys()])
# Install everything in the right location and take care of Linux (default) and
# FreeBSD systems.
USR = "/usr"
ETC = "/etc"
USR_LIB_EXEC = "/usr/lib"
+LIB = "/lib"
if os.uname()[0] == 'FreeBSD':
USR = "/usr/local"
USR_LIB_EXEC = "/usr/local/lib"
@@ -120,9 +133,8 @@ class InitsysInstallData(install):
user_options = install.user_options + [
# This will magically show up in member variable 'init_sys'
('init-system=', None,
- ('init system(s) to configure (%s) [default: None]') %
- (", ".join(INITSYS_TYPES))
- ),
+ ('init system(s) to configure (%s) [default: None]' %
+ (", ".join(INITSYS_TYPES)))),
]
def initialize_options(self):
@@ -136,7 +148,8 @@ class InitsysInstallData(install):
self.init_system = self.init_system.split(",")
if len(self.init_system) == 0:
- raise DistutilsArgError(("You must specify one of (%s) when"
+ raise DistutilsArgError(
+ ("You must specify one of (%s) when"
" specifying init system(s)!") % (", ".join(INITSYS_TYPES)))
bad = [f for f in self.init_system if f not in INITSYS_TYPES]
@@ -144,9 +157,13 @@ class InitsysInstallData(install):
raise DistutilsArgError(
"Invalid --init-system: %s" % (','.join(bad)))
- for sys in self.init_system:
- self.distribution.data_files.append(
- (INITSYS_ROOTS[sys], INITSYS_FILES[sys]))
+ for system in self.init_system:
+ # add data files for anything that starts with '<system>.'
+ datakeys = [k for k in INITSYS_ROOTS
+ if k.partition(".")[0] == system]
+ for k in datakeys:
+ self.distribution.data_files.append(
+ (INITSYS_ROOTS[k], INITSYS_FILES[k]))
# Force that command to reinitalize (with new file list)
self.distribution.reinitialize_command('install_data', True)
@@ -166,6 +183,8 @@ else:
[f for f in glob('doc/examples/*') if is_f(f)]),
(USR + '/share/doc/cloud-init/examples/seed',
[f for f in glob('doc/examples/seed/*') if is_f(f)]),
+ (LIB + '/udev/rules.d', [f for f in glob('udev/*.rules')]),
+ (LIB + '/udev', ['udev/cloud-init-wait']),
]
# Use a subclass for install that handles
# adding on the right init system configuration files
@@ -174,18 +193,23 @@ else:
}
-setuptools.setup(name='cloud-init',
- version=get_version(),
- description='EC2 initialisation magic',
- author='Scott Moser',
- author_email='scott.moser@canonical.com',
- url='http://launchpad.net/cloud-init/',
- packages=setuptools.find_packages(exclude=['tests']),
- scripts=['bin/cloud-init',
- 'tools/cloud-init-per',
- ],
- license='GPLv3',
- data_files=data_files,
- install_requires=read_requires(),
- cmdclass=cmdclass,
- )
+requirements = read_requires()
+if sys.version_info < (3,):
+ requirements.append('cheetah')
+
+
+setuptools.setup(
+ name='cloud-init',
+ version=get_version(),
+ description='EC2 initialisation magic',
+ author='Scott Moser',
+ author_email='scott.moser@canonical.com',
+ url='http://launchpad.net/cloud-init/',
+ packages=setuptools.find_packages(exclude=['tests']),
+ scripts=['bin/cloud-init',
+ 'tools/cloud-init-per'],
+ license='GPLv3',
+ data_files=data_files,
+ install_requires=requirements,
+ cmdclass=cmdclass,
+ )
diff --git a/systemd/cloud-config.service b/systemd/cloud-config.service
index 41a86147..45d2a63b 100644
--- a/systemd/cloud-config.service
+++ b/systemd/cloud-config.service
@@ -1,8 +1,7 @@
[Unit]
Description=Apply the settings specified in cloud-config
-After=network.target syslog.target cloud-config.target
-Requires=cloud-config.target
-Wants=network.target
+After=network-online.target cloud-config.target syslog.target
+Wants=network-online.target cloud-config.target
[Service]
Type=oneshot
@@ -14,4 +13,4 @@ TimeoutSec=0
StandardOutput=journal+console
[Install]
-WantedBy=multi-user.target
+WantedBy=cloud-init.target
diff --git a/systemd/cloud-config.target b/systemd/cloud-config.target
index 28f5bcf1..ae9b7d02 100644
--- a/systemd/cloud-config.target
+++ b/systemd/cloud-config.target
@@ -7,4 +7,5 @@
[Unit]
Description=Cloud-config availability
-Requires=cloud-init-local.service cloud-init.service
+Wants=cloud-init-local.service cloud-init.service
+After=cloud-init-local.service cloud-init.service
diff --git a/systemd/cloud-final.service b/systemd/cloud-final.service
index ef0f52b9..bfb08d4a 100644
--- a/systemd/cloud-final.service
+++ b/systemd/cloud-final.service
@@ -1,17 +1,17 @@
[Unit]
Description=Execute cloud user/final scripts
-After=network.target syslog.target cloud-config.service rc-local.service
-Requires=cloud-config.target
-Wants=network.target
+After=network-online.target cloud-config.service syslog.target rc-local.service
+Wants=network-online.target cloud-config.service
[Service]
Type=oneshot
ExecStart=/usr/bin/cloud-init modules --mode=final
RemainAfterExit=yes
TimeoutSec=0
+KillMode=process
# Output needs to appear in instance console output
StandardOutput=journal+console
[Install]
-WantedBy=multi-user.target
+WantedBy=cloud-init.target
diff --git a/systemd/cloud-init-generator b/systemd/cloud-init-generator
new file mode 100755
index 00000000..ae286d58
--- /dev/null
+++ b/systemd/cloud-init-generator
@@ -0,0 +1,133 @@
+#!/bin/sh
+set -f
+
+LOG=""
+DEBUG_LEVEL=1
+LOG_D="/run/cloud-init"
+ENABLE="enabled"
+DISABLE="disabled"
+CLOUD_SYSTEM_TARGET="/lib/systemd/system/cloud-init.target"
+CLOUD_TARGET_NAME="cloud-init.target"
+# lxc sets 'container', but lets make that explicitly a global
+CONTAINER="${container}"
+
+debug() {
+ local lvl="$1"
+ shift
+ [ "$lvl" -gt "$DEBUG_LEVEL" ] && return
+ if [ -z "$LOG" ]; then
+ local log="$LOG_D/${0##*/}.log"
+ { [ -d "$LOG_D" ] || mkdir -p "$LOG_D"; } &&
+ { : > "$log"; } >/dev/null 2>&1 && LOG="$log" ||
+ LOG="/dev/kmsg"
+ fi
+ echo "$@" >> "$LOG"
+}
+
+etc_file() {
+ local pprefix="${1:-/etc/cloud/cloud-init.}"
+ _RET="unset"
+ [ -f "${pprefix}$ENABLE" ] && _RET="$ENABLE" && return 0
+ [ -f "${pprefix}$DISABLE" ] && _RET="$DISABLE" && return 0
+ return 0
+}
+
+read_proc_cmdline() {
+ # return /proc/cmdline for non-container, and /proc/1/cmdline for container
+ local ctname="systemd"
+ if [ -n "$CONTAINER" ] && ctname=$CONTAINER ||
+ systemd-detect-virt --container --quiet; then
+ if { _RET=$(tr '\0' ' ' < /proc/1/cmdline); } 2>/dev/null; then
+ _RET_MSG="container[$ctname]: pid 1 cmdline"
+ return
+ fi
+ _RET=""
+ _RET_MSG="container[$ctname]: pid 1 cmdline not available"
+ return 0
+ fi
+
+ _RET_MSG="/proc/cmdline"
+ read _RET < /proc/cmdline
+}
+
+kernel_cmdline() {
+ local cmdline="" tok=""
+ if [ -n "${KERNEL_CMDLINE+x}" ]; then
+ # use KERNEL_CMDLINE if present in environment even if empty
+ cmdline=${KERNEL_CMDLINE}
+ debug 1 "kernel command line from env KERNEL_CMDLINE: $cmdline"
+ elif read_proc_cmdline; then
+ read_proc_cmdline && cmdline="$_RET"
+ debug 1 "kernel command line ($_RET_MSG): $cmdline"
+ fi
+ _RET="unset"
+ cmdline=" $cmdline "
+ tok=${cmdline##* cloud-init=}
+ [ "$tok" = "$cmdline" ] && _RET="unset"
+ tok=${tok%% *}
+ [ "$tok" = "$ENABLE" -o "$tok" = "$DISABLE" ] && _RET="$tok"
+ return 0
+}
+
+default() {
+ _RET="$ENABLE"
+}
+
+main() {
+ local normal_d="$1" early_d="$2" late_d="$3"
+ local target_name="multi-user.target" gen_d="$early_d"
+ local link_path="$gen_d/${target_name}.wants/${CLOUD_TARGET_NAME}"
+
+ debug 1 "$0 normal=$normal_d early=$early_d late=$late_d"
+ debug 2 "$0 $*"
+
+ local search result="error" ret=""
+ for search in kernel_cmdline etc_file default; do
+ if $search; then
+ debug 1 "$search found $_RET"
+ [ "$_RET" = "$ENABLE" -o "$_RET" = "$DISABLE" ] &&
+ result=$_RET && break
+ else
+ ret=$?
+ debug 0 "search $search returned $ret"
+ fi
+ done
+
+ if [ "$result" = "$ENABLE" ]; then
+ if [ -e "$link_path" ]; then
+ debug 1 "already enabled: no change needed"
+ else
+ [ -d "${link_path%/*}" ] || mkdir -p "${link_path%/*}" ||
+ debug 0 "failed to make dir $link_path"
+ if ln -snf "$CLOUD_SYSTEM_TARGET" "$link_path"; then
+ debug 1 "enabled via $link_path -> $CLOUD_SYSTEM_TARGET"
+ else
+ ret=$?
+ debug 0 "[$ret] enable failed:" \
+ "ln $CLOUD_SYSTEM_TARGET $link_path"
+ fi
+ fi
+ # this touches /run/cloud-init/enabled, which is read by
+ # udev/cloud-init-wait. If not present, it will exit quickly.
+ touch "$LOG_D/$ENABLE"
+ elif [ "$result" = "$DISABLE" ]; then
+ if [ -f "$link_path" ]; then
+ if rm -f "$link_path"; then
+ debug 1 "disabled. removed existing $link_path"
+ else
+ ret=$?
+ debug 0 "[$ret] disable failed, remove $link_path"
+ fi
+ else
+ debug 1 "already disabled: no change needed [no $link_path]"
+ fi
+ else
+ debug 0 "unexpected result '$result'"
+ ret=3
+ fi
+ return $ret
+}
+
+main "$@"
+
+# vi: ts=4 expandtab
diff --git a/systemd/cloud-init-local.service b/systemd/cloud-init-local.service
index a31985c6..b19eeaee 100644
--- a/systemd/cloud-init-local.service
+++ b/systemd/cloud-init-local.service
@@ -1,11 +1,17 @@
[Unit]
Description=Initial cloud-init job (pre-networking)
+DefaultDependencies=no
Wants=local-fs.target
+Wants=network-pre.target
After=local-fs.target
+Conflicts=shutdown.target
+Before=network-pre.target
+Before=shutdown.target
[Service]
Type=oneshot
ExecStart=/usr/bin/cloud-init init --local
+ExecStart=/bin/touch /run/cloud-init/network-config-ready
RemainAfterExit=yes
TimeoutSec=0
@@ -13,4 +19,4 @@ TimeoutSec=0
StandardOutput=journal+console
[Install]
-WantedBy=multi-user.target
+WantedBy=cloud-init.target
diff --git a/systemd/cloud-init.service b/systemd/cloud-init.service
index 398b90ea..6fb655e6 100644
--- a/systemd/cloud-init.service
+++ b/systemd/cloud-init.service
@@ -1,8 +1,8 @@
[Unit]
Description=Initial cloud-init job (metadata service crawler)
-After=local-fs.target network.target cloud-init-local.service
-Before=sshd.service sshd-keygen.service systemd-user-sessions.service
-Requires=network.target
+After=cloud-init-local.service networking.service
+Before=network-online.target sshd.service sshd-keygen.service systemd-user-sessions.service
+Requires=networking.service
Wants=local-fs.target cloud-init-local.service sshd.service sshd-keygen.service
[Service]
@@ -15,4 +15,4 @@ TimeoutSec=0
StandardOutput=journal+console
[Install]
-WantedBy=multi-user.target
+WantedBy=cloud-init.target
diff --git a/systemd/cloud-init.target b/systemd/cloud-init.target
new file mode 100644
index 00000000..a63babb0
--- /dev/null
+++ b/systemd/cloud-init.target
@@ -0,0 +1,6 @@
+# cloud-init target is enabled by cloud-init-generator
+# To disable it you can either:
+# a.) boot with kernel cmdline of 'cloudinit=disabled'
+# b.) touch a file /etc/cloud/cloud-init.disabled
+[Unit]
+Description=Cloud-init target
diff --git a/sysvinit/redhat/cloud-init-local b/sysvinit/redhat/cloud-init-local
index b53e0db2..b9caedbd 100755
--- a/sysvinit/redhat/cloud-init-local
+++ b/sysvinit/redhat/cloud-init-local
@@ -23,9 +23,12 @@
# See: http://www.novell.com/coolsolutions/feature/15380.html
# Also based on dhcpd in RHEL (for comparison)
+# Bring this up before network, S10
+#chkconfig: 2345 09 91
+
### BEGIN INIT INFO
# Provides: cloud-init-local
-# Required-Start: $local_fs $remote_fs
+# Required-Start: $local_fs
# Should-Start: $time
# Required-Stop:
# Should-Stop:
diff --git a/templates/resolv.conf.tmpl b/templates/resolv.conf.tmpl
index 1300156c..bfae80db 100644
--- a/templates/resolv.conf.tmpl
+++ b/templates/resolv.conf.tmpl
@@ -24,7 +24,7 @@ sortlist {% for sort in sortlist %}{{sort}} {% endfor %}
{% if options or flags %}
options {% for flag in flags %}{{flag}} {% endfor %}
-{% for key, value in options.iteritems() -%}
+{% for key, value in options.items() -%}
{{key}}:{{value}}
{% endfor %}
{% endif %}
diff --git a/templates/sources.list.ubuntu.tmpl b/templates/sources.list.ubuntu.tmpl
index 4b1b019a..d8799726 100644
--- a/templates/sources.list.ubuntu.tmpl
+++ b/templates/sources.list.ubuntu.tmpl
@@ -9,13 +9,13 @@
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
-deb {{mirror}} {{codename}} main
-deb-src {{mirror}} {{codename}} main
+deb {{mirror}} {{codename}} main restricted
+deb-src {{mirror}} {{codename}} main restricted
## Major bug fix updates produced after the final release of the
## distribution.
-deb {{mirror}} {{codename}}-updates main
-deb-src {{mirror}} {{codename}}-updates main
+deb {{mirror}} {{codename}}-updates main restricted
+deb-src {{mirror}} {{codename}}-updates main restricted
## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team. Also, please note that software in universe WILL NOT receive any
@@ -26,24 +26,29 @@ deb {{mirror}} {{codename}}-updates universe
deb-src {{mirror}} {{codename}}-updates universe
## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
-## team, and may not be under a free licence. Please satisfy yourself as to
+## team, and may not be under a free licence. Please satisfy yourself as to
## your rights to use the software. Also, please note that software in
## multiverse WILL NOT receive any review or updates from the Ubuntu
## security team.
-# deb {{mirror}} {{codename}} multiverse
-# deb-src {{mirror}} {{codename}} multiverse
-# deb {{mirror}} {{codename}}-updates multiverse
-# deb-src {{mirror}} {{codename}}-updates multiverse
+deb {{mirror}} {{codename}} multiverse
+deb-src {{mirror}} {{codename}} multiverse
+deb {{mirror}} {{codename}}-updates multiverse
+deb-src {{mirror}} {{codename}}-updates multiverse
-## Uncomment the following two lines to add software from the 'backports'
-## repository.
## N.B. software from this repository may not have been tested as
## extensively as that contained in the main release, although it includes
## newer versions of some applications which may provide useful features.
## Also, please note that software in backports WILL NOT receive any review
## or updates from the Ubuntu security team.
-# deb {{mirror}} {{codename}}-backports main restricted universe multiverse
-# deb-src {{mirror}} {{codename}}-backports main restricted universe multiverse
+deb {{mirror}} {{codename}}-backports main restricted universe multiverse
+deb-src {{mirror}} {{codename}}-backports main restricted universe multiverse
+
+deb {{security}} {{codename}}-security main restricted
+deb-src {{security}} {{codename}}-security main restricted
+deb {{security}} {{codename}}-security universe
+deb-src {{security}} {{codename}}-security universe
+deb {{security}} {{codename}}-security multiverse
+deb-src {{security}} {{codename}}-security multiverse
## Uncomment the following two lines to add software from Canonical's
## 'partner' repository.
@@ -51,10 +56,3 @@ deb-src {{mirror}} {{codename}}-updates universe
## respective vendors as a service to Ubuntu users.
# deb http://archive.canonical.com/ubuntu {{codename}} partner
# deb-src http://archive.canonical.com/ubuntu {{codename}} partner
-
-deb {{security}} {{codename}}-security main
-deb-src {{security}} {{codename}}-security main
-deb {{security}} {{codename}}-security universe
-deb-src {{security}} {{codename}}-security universe
-# deb {{security}} {{codename}}-security multiverse
-# deb-src {{security}} {{codename}}-security multiverse
diff --git a/test-requirements.txt b/test-requirements.txt
index 230f0404..9b3d07c5 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,6 +1,7 @@
httpretty>=0.7.1
mock
-mocker
nose
pep8==1.5.7
pyflakes
+contextlib2
+setuptools
diff --git a/tests/data/vmware/cust-dhcp-2nic.cfg b/tests/data/vmware/cust-dhcp-2nic.cfg
new file mode 100644
index 00000000..f687311a
--- /dev/null
+++ b/tests/data/vmware/cust-dhcp-2nic.cfg
@@ -0,0 +1,34 @@
+[NETWORK]
+NETWORKING = yes
+BOOTPROTO = dhcp
+HOSTNAME = myhost1
+DOMAINNAME = eng.vmware.com
+
+[NIC-CONFIG]
+NICS = NIC1,NIC2
+
+[NIC1]
+MACADDR = 00:50:56:a6:8c:08
+ONBOOT = yes
+IPv4_MODE = BACKWARDS_COMPATIBLE
+BOOTPROTO = dhcp
+
+[NIC2]
+MACADDR = 00:50:56:a6:5a:de
+ONBOOT = yes
+IPv4_MODE = BACKWARDS_COMPATIBLE
+BOOTPROTO = dhcp
+
+# some random comment
+
+[PASSWORD]
+# secret
+-PASS = c2VjcmV0Cg==
+
+[DNS]
+DNSFROMDHCP=yes
+SUFFIX|1 = eng.vmware.com
+
+[DATETIME]
+TIMEZONE = Africa/Abidjan
+UTC = yes
diff --git a/tests/data/vmware/cust-static-2nic.cfg b/tests/data/vmware/cust-static-2nic.cfg
new file mode 100644
index 00000000..0d80c2c4
--- /dev/null
+++ b/tests/data/vmware/cust-static-2nic.cfg
@@ -0,0 +1,39 @@
+[NETWORK]
+NETWORKING = yes
+BOOTPROTO = dhcp
+HOSTNAME = myhost1
+DOMAINNAME = eng.vmware.com
+
+[NIC-CONFIG]
+NICS = NIC1,NIC2
+
+[NIC1]
+MACADDR = 00:50:56:a6:8c:08
+ONBOOT = yes
+IPv4_MODE = BACKWARDS_COMPATIBLE
+BOOTPROTO = static
+IPADDR = 10.20.87.154
+NETMASK = 255.255.252.0
+GATEWAY = 10.20.87.253, 10.20.87.105
+IPv6ADDR|1 = fc00:10:20:87::154
+IPv6NETMASK|1 = 64
+IPv6GATEWAY|1 = fc00:10:20:87::253
+[NIC2]
+MACADDR = 00:50:56:a6:ef:7d
+ONBOOT = yes
+IPv4_MODE = BACKWARDS_COMPATIBLE
+BOOTPROTO = static
+IPADDR = 192.168.6.102
+NETMASK = 255.255.0.0
+GATEWAY = 192.168.0.10
+
+[DNS]
+DNSFROMDHCP=no
+SUFFIX|1 = eng.vmware.com
+SUFFIX|2 = proxy.vmware.com
+NAMESERVER|1 = 10.20.145.1
+NAMESERVER|2 = 10.20.145.2
+
+[DATETIME]
+TIMEZONE = Africa/Abidjan
+UTC = yes
diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py
index 52305397..7f4b8784 100644
--- a/tests/unittests/helpers.py
+++ b/tests/unittests/helpers.py
@@ -1,25 +1,35 @@
+from __future__ import print_function
+
+import functools
import os
import sys
+import shutil
+import tempfile
import unittest
-from contextlib import contextmanager
+import six
-from mocker import Mocker
-from mocker import MockerTestCase
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
from cloudinit import helpers as ch
from cloudinit import util
-import shutil
-
# Used for detecting different python versions
PY2 = False
PY26 = False
PY27 = False
PY3 = False
+FIX_HTTPRETTY = False
_PY_VER = sys.version_info
-_PY_MAJOR, _PY_MINOR = _PY_VER[0:2]
+_PY_MAJOR, _PY_MINOR, _PY_MICRO = _PY_VER[0:3]
if (_PY_MAJOR, _PY_MINOR) <= (2, 6):
if (_PY_MAJOR, _PY_MINOR) == (2, 6):
PY26 = True
@@ -31,10 +41,24 @@ else:
PY2 = True
if (_PY_MAJOR, _PY_MINOR) >= (3, 0):
PY3 = True
+ if _PY_MINOR == 4 and _PY_MICRO < 3:
+ FIX_HTTPRETTY = True
if PY26:
- # For now add these on, taken from python 2.7 + slightly adjusted
+ # For now add these on, taken from python 2.7 + slightly adjusted. Drop
+ # all this once Python 2.6 is dropped as a minimum requirement.
class TestCase(unittest.TestCase):
+ def setUp(self):
+ super(TestCase, self).setUp()
+ self.__all_cleanups = ExitStack()
+
+ def tearDown(self):
+ self.__all_cleanups.close()
+ unittest.TestCase.tearDown(self)
+
+ def addCleanup(self, function, *args, **kws):
+ self.__all_cleanups.callback(function, *args, **kws)
+
def assertIs(self, expr1, expr2, msg=None):
if expr1 is not expr2:
standardMsg = '%r is not %r' % (expr1, expr2)
@@ -57,10 +81,17 @@ if PY26:
standardMsg = standardMsg % (value)
self.fail(self._formatMessage(msg, standardMsg))
+ def assertIsInstance(self, obj, cls, msg=None):
+ """Same as self.assertTrue(isinstance(obj, cls)), with a nicer
+ default message."""
+ if not isinstance(obj, cls):
+ standardMsg = '%s is not an instance of %r' % (repr(obj), cls)
+ self.fail(self._formatMessage(msg, standardMsg))
+
def assertDictContainsSubset(self, expected, actual, msg=None):
missing = []
mismatched = []
- for k, v in expected.iteritems():
+ for k, v in expected.items():
if k not in actual:
missing.append(k)
elif actual[k] != v:
@@ -86,17 +117,6 @@ else:
pass
-@contextmanager
-def mocker(verify_calls=True):
- m = Mocker()
- try:
- yield m
- finally:
- m.restore()
- if verify_calls:
- m.verify()
-
-
# Makes the old path start
# with new base instead of whatever
# it previously had
@@ -121,14 +141,19 @@ def retarget_many_wrapper(new_base, am, old_func):
nam = len(n_args)
for i in range(0, nam):
path = args[i]
- n_args[i] = rebase_path(path, new_base)
+ # patchOS() wraps various os and os.path functions, however in
+ # Python 3 some of these now accept file-descriptors (integers).
+ # That breaks rebase_path() so in lieu of a better solution, just
+ # don't rebase if we get a fd.
+ if isinstance(path, six.string_types):
+ n_args[i] = rebase_path(path, new_base)
return old_func(*n_args, **kwds)
return wrapper
-class ResourceUsingTestCase(MockerTestCase):
- def __init__(self, methodName="runTest"):
- MockerTestCase.__init__(self, methodName)
+class ResourceUsingTestCase(TestCase):
+ def setUp(self):
+ super(ResourceUsingTestCase, self).setUp()
self.resource_path = None
def resourceLocation(self, subname=None):
@@ -156,17 +181,23 @@ class ResourceUsingTestCase(MockerTestCase):
return fh.read()
def getCloudPaths(self):
+ tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, tmpdir)
cp = ch.Paths({
- 'cloud_dir': self.makeDir(),
+ 'cloud_dir': tmpdir,
'templates_dir': self.resourceLocation(),
})
return cp
class FilesystemMockingTestCase(ResourceUsingTestCase):
- def __init__(self, methodName="runTest"):
- ResourceUsingTestCase.__init__(self, methodName)
- self.patched_funcs = []
+ def setUp(self):
+ super(FilesystemMockingTestCase, self).setUp()
+ self.patched_funcs = ExitStack()
+
+ def tearDown(self):
+ self.patched_funcs.close()
+ ResourceUsingTestCase.tearDown(self)
def replicateTestRoot(self, example_root, target_root):
real_root = self.resourceLocation()
@@ -180,15 +211,6 @@ class FilesystemMockingTestCase(ResourceUsingTestCase):
make_path = util.abs_join(make_path, f)
shutil.copy(real_path, make_path)
- def tearDown(self):
- self.restore()
- ResourceUsingTestCase.tearDown(self)
-
- def restore(self):
- for (mod, f, func) in self.patched_funcs:
- setattr(mod, f, func)
- self.patched_funcs = []
-
def patchUtils(self, new_root):
patch_funcs = {
util: [('write_file', 1),
@@ -205,8 +227,8 @@ class FilesystemMockingTestCase(ResourceUsingTestCase):
for (f, am) in funcs:
func = getattr(mod, f)
trap_func = retarget_many_wrapper(new_root, am, func)
- setattr(mod, f, trap_func)
- self.patched_funcs.append((mod, f, func))
+ self.patched_funcs.enter_context(
+ mock.patch.object(mod, f, trap_func))
# Handle subprocess calls
func = getattr(util, 'subp')
@@ -214,28 +236,73 @@ class FilesystemMockingTestCase(ResourceUsingTestCase):
def nsubp(*_args, **_kwargs):
return ('', '')
- setattr(util, 'subp', nsubp)
- self.patched_funcs.append((util, 'subp', func))
+ self.patched_funcs.enter_context(
+ mock.patch.object(util, 'subp', nsubp))
def null_func(*_args, **_kwargs):
return None
for f in ['chownbyid', 'chownbyname']:
- func = getattr(util, f)
- setattr(util, f, null_func)
- self.patched_funcs.append((util, f, func))
+ self.patched_funcs.enter_context(
+ mock.patch.object(util, f, null_func))
def patchOS(self, new_root):
patch_funcs = {
- os.path: ['isfile', 'exists', 'islink', 'isdir'],
- os: ['listdir'],
+ os.path: [('isfile', 1), ('exists', 1),
+ ('islink', 1), ('isdir', 1)],
+ os: [('listdir', 1), ('mkdir', 1),
+ ('lstat', 1), ('symlink', 2)],
}
for (mod, funcs) in patch_funcs.items():
- for f in funcs:
+ for f, nargs in funcs:
func = getattr(mod, f)
- trap_func = retarget_many_wrapper(new_root, 1, func)
- setattr(mod, f, trap_func)
- self.patched_funcs.append((mod, f, func))
+ trap_func = retarget_many_wrapper(new_root, nargs, func)
+ self.patched_funcs.enter_context(
+ mock.patch.object(mod, f, trap_func))
+
+ def patchOpen(self, new_root):
+ trap_func = retarget_many_wrapper(new_root, 1, open)
+ name = 'builtins.open' if PY3 else '__builtin__.open'
+ self.patched_funcs.enter_context(mock.patch(name, trap_func))
+
+ def patchStdoutAndStderr(self, stdout=None, stderr=None):
+ if stdout is not None:
+ self.patched_funcs.enter_context(
+ mock.patch.object(sys, 'stdout', stdout))
+ if stderr is not None:
+ self.patched_funcs.enter_context(
+ mock.patch.object(sys, 'stderr', stderr))
+
+
+def import_httpretty():
+ """Import HTTPretty and monkey patch Python 3.4 issue.
+ See https://github.com/gabrielfalcao/HTTPretty/pull/193 and
+ as well as https://github.com/gabrielfalcao/HTTPretty/issues/221.
+
+ Lifted from
+ https://github.com/inveniosoftware/datacite/blob/master/tests/helpers.py
+ """
+ if not FIX_HTTPRETTY:
+ import httpretty
+ else:
+ import socket
+ old_SocketType = socket.SocketType
+
+ import httpretty
+ from httpretty import core
+
+ def sockettype_patch(f):
+ @functools.wraps(f)
+ def inner(*args, **kwargs):
+ f(*args, **kwargs)
+ socket.SocketType = old_SocketType
+ socket.__dict__['SocketType'] = old_SocketType
+ return inner
+
+ core.httpretty.disable = sockettype_patch(
+ httpretty.httpretty.disable
+ )
+ return httpretty
class HttprettyTestCase(TestCase):
@@ -256,7 +323,25 @@ class HttprettyTestCase(TestCase):
def populate_dir(path, files):
if not os.path.exists(path):
os.makedirs(path)
- for (name, content) in files.iteritems():
- with open(os.path.join(path, name), "w") as fp:
- fp.write(content)
+ for (name, content) in files.items():
+ with open(os.path.join(path, name), "wb") as fp:
+ if isinstance(content, six.binary_type):
+ fp.write(content)
+ else:
+ fp.write(content.encode('utf-8'))
fp.close()
+
+
+try:
+ skipIf = unittest.skipIf
+except AttributeError:
+ # Python 2.6. Doesn't have to be high fidelity.
+ def skipIf(condition, reason):
+ def decorator(func):
+ def wrapper(*args, **kws):
+ if condition:
+ return func(*args, **kws)
+ else:
+ print(reason, file=sys.stderr)
+ return wrapper
+ return decorator
diff --git a/tests/unittests/test__init__.py b/tests/unittests/test__init__.py
index 17965488..153f1658 100644
--- a/tests/unittests/test__init__.py
+++ b/tests/unittests/test__init__.py
@@ -1,14 +1,25 @@
import os
-
-from mocker import MockerTestCase, ARGS, KWARGS
+import shutil
+import tempfile
+import unittest
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
from cloudinit import handlers
from cloudinit import helpers
-from cloudinit import importer
from cloudinit import settings
from cloudinit import url_helper
from cloudinit import util
+from .helpers import TestCase
+
class FakeModule(handlers.Handler):
def __init__(self):
@@ -22,76 +33,73 @@ class FakeModule(handlers.Handler):
pass
-class TestWalkerHandleHandler(MockerTestCase):
+class TestWalkerHandleHandler(TestCase):
def setUp(self):
-
- MockerTestCase.setUp(self)
+ super(TestWalkerHandleHandler, self).setUp()
+ tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, tmpdir)
self.data = {
"handlercount": 0,
"frequency": "",
- "handlerdir": self.makeDir(),
+ "handlerdir": tmpdir,
"handlers": helpers.ContentHandlers(),
"data": None}
self.expected_module_name = "part-handler-%03d" % (
self.data["handlercount"],)
expected_file_name = "%s.py" % self.expected_module_name
- expected_file_fullname = os.path.join(self.data["handlerdir"],
- expected_file_name)
+ self.expected_file_fullname = os.path.join(
+ self.data["handlerdir"], expected_file_name)
self.module_fake = FakeModule()
self.ctype = None
self.filename = None
self.payload = "dummy payload"
- # Mock the write_file function
- write_file_mock = self.mocker.replace(util.write_file,
- passthrough=False)
- write_file_mock(expected_file_fullname, self.payload, 0600)
+ # Mock the write_file() function. We'll assert that it got called as
+ # expected in each of the individual tests.
+ resources = ExitStack()
+ self.addCleanup(resources.close)
+ self.write_file_mock = resources.enter_context(
+ mock.patch('cloudinit.util.write_file'))
def test_no_errors(self):
"""Payload gets written to file and added to C{pdata}."""
- import_mock = self.mocker.replace(importer.import_module,
- passthrough=False)
- import_mock(self.expected_module_name)
- self.mocker.result(self.module_fake)
- self.mocker.replay()
-
- handlers.walker_handle_handler(self.data, self.ctype, self.filename,
- self.payload)
-
- self.assertEqual(1, self.data["handlercount"])
+ with mock.patch('cloudinit.importer.import_module',
+ return_value=self.module_fake) as mockobj:
+ handlers.walker_handle_handler(self.data, self.ctype,
+ self.filename, self.payload)
+ mockobj.assert_called_once_with(self.expected_module_name)
+ self.write_file_mock.assert_called_once_with(
+ self.expected_file_fullname, self.payload, 0o600)
+ self.assertEqual(self.data['handlercount'], 1)
def test_import_error(self):
"""Module import errors are logged. No handler added to C{pdata}."""
- import_mock = self.mocker.replace(importer.import_module,
- passthrough=False)
- import_mock(self.expected_module_name)
- self.mocker.throw(ImportError())
- self.mocker.replay()
-
- handlers.walker_handle_handler(self.data, self.ctype, self.filename,
- self.payload)
-
- self.assertEqual(0, self.data["handlercount"])
+ with mock.patch('cloudinit.importer.import_module',
+ side_effect=ImportError) as mockobj:
+ handlers.walker_handle_handler(self.data, self.ctype,
+ self.filename, self.payload)
+ mockobj.assert_called_once_with(self.expected_module_name)
+ self.write_file_mock.assert_called_once_with(
+ self.expected_file_fullname, self.payload, 0o600)
+ self.assertEqual(self.data['handlercount'], 0)
def test_attribute_error(self):
"""Attribute errors are logged. No handler added to C{pdata}."""
- import_mock = self.mocker.replace(importer.import_module,
- passthrough=False)
- import_mock(self.expected_module_name)
- self.mocker.result(self.module_fake)
- self.mocker.throw(AttributeError())
- self.mocker.replay()
-
- handlers.walker_handle_handler(self.data, self.ctype, self.filename,
- self.payload)
-
- self.assertEqual(0, self.data["handlercount"])
+ with mock.patch('cloudinit.importer.import_module',
+ side_effect=AttributeError,
+ return_value=self.module_fake) as mockobj:
+ handlers.walker_handle_handler(self.data, self.ctype,
+ self.filename, self.payload)
+ mockobj.assert_called_once_with(self.expected_module_name)
+ self.write_file_mock.assert_called_once_with(
+ self.expected_file_fullname, self.payload, 0o600)
+ self.assertEqual(self.data['handlercount'], 0)
-class TestHandlerHandlePart(MockerTestCase):
+class TestHandlerHandlePart(unittest.TestCase):
def setUp(self):
self.data = "fake data"
@@ -108,123 +116,105 @@ class TestHandlerHandlePart(MockerTestCase):
C{handle_part} is called without C{frequency} for
C{handler_version} == 1.
"""
- mod_mock = self.mocker.mock()
- getattr(mod_mock, "frequency")
- self.mocker.result(settings.PER_INSTANCE)
- getattr(mod_mock, "handler_version")
- self.mocker.result(1)
- mod_mock.handle_part(self.data, self.ctype, self.filename,
- self.payload)
- self.mocker.replay()
-
- handlers.run_part(mod_mock, self.data, self.filename,
- self.payload, self.frequency, self.headers)
+ mod_mock = mock.Mock(frequency=settings.PER_INSTANCE,
+ handler_version=1)
+ handlers.run_part(mod_mock, self.data, self.filename, self.payload,
+ self.frequency, self.headers)
+ # Assert that the handle_part() method of the mock object got
+ # called with the expected arguments.
+ mod_mock.handle_part.assert_called_once_with(
+ self.data, self.ctype, self.filename, self.payload)
def test_normal_version_2(self):
"""
C{handle_part} is called with C{frequency} for
C{handler_version} == 2.
"""
- mod_mock = self.mocker.mock()
- getattr(mod_mock, "frequency")
- self.mocker.result(settings.PER_INSTANCE)
- getattr(mod_mock, "handler_version")
- self.mocker.result(2)
- mod_mock.handle_part(self.data, self.ctype, self.filename,
- self.payload, self.frequency)
- self.mocker.replay()
-
- handlers.run_part(mod_mock, self.data, self.filename,
- self.payload, self.frequency, self.headers)
+ mod_mock = mock.Mock(frequency=settings.PER_INSTANCE,
+ handler_version=2)
+ handlers.run_part(mod_mock, self.data, self.filename, self.payload,
+ self.frequency, self.headers)
+ # Assert that the handle_part() method of the mock object got
+ # called with the expected arguments.
+ mod_mock.handle_part.assert_called_once_with(
+ self.data, self.ctype, self.filename, self.payload,
+ settings.PER_INSTANCE)
def test_modfreq_per_always(self):
"""
C{handle_part} is called regardless of frequency if nofreq is always.
"""
self.frequency = "once"
- mod_mock = self.mocker.mock()
- getattr(mod_mock, "frequency")
- self.mocker.result(settings.PER_ALWAYS)
- getattr(mod_mock, "handler_version")
- self.mocker.result(1)
- mod_mock.handle_part(self.data, self.ctype, self.filename,
- self.payload)
- self.mocker.replay()
-
- handlers.run_part(mod_mock, self.data, self.filename,
- self.payload, self.frequency, self.headers)
+ mod_mock = mock.Mock(frequency=settings.PER_ALWAYS,
+ handler_version=1)
+ handlers.run_part(mod_mock, self.data, self.filename, self.payload,
+ self.frequency, self.headers)
+ # Assert that the handle_part() method of the mock object got
+ # called with the expected arguments.
+ mod_mock.handle_part.assert_called_once_with(
+ self.data, self.ctype, self.filename, self.payload)
def test_no_handle_when_modfreq_once(self):
"""C{handle_part} is not called if frequency is once."""
self.frequency = "once"
- mod_mock = self.mocker.mock()
- getattr(mod_mock, "frequency")
- self.mocker.result(settings.PER_ONCE)
- self.mocker.replay()
-
- handlers.run_part(mod_mock, self.data, self.filename,
- self.payload, self.frequency, self.headers)
+ mod_mock = mock.Mock(frequency=settings.PER_ONCE)
+ handlers.run_part(mod_mock, self.data, self.filename, self.payload,
+ self.frequency, self.headers)
+ self.assertEqual(0, mod_mock.handle_part.call_count)
def test_exception_is_caught(self):
"""Exceptions within C{handle_part} are caught and logged."""
- mod_mock = self.mocker.mock()
- getattr(mod_mock, "frequency")
- self.mocker.result(settings.PER_INSTANCE)
- getattr(mod_mock, "handler_version")
- self.mocker.result(1)
- mod_mock.handle_part(self.data, self.ctype, self.filename,
- self.payload)
- self.mocker.throw(Exception())
- self.mocker.replay()
+ mod_mock = mock.Mock(frequency=settings.PER_INSTANCE,
+ handler_version=1)
+ mod_mock.handle_part.side_effect = Exception
+ try:
+ handlers.run_part(mod_mock, self.data, self.filename,
+ self.payload, self.frequency, self.headers)
+ except Exception:
+ self.fail("Exception was not caught in handle_part")
- handlers.run_part(mod_mock, self.data, self.filename,
- self.payload, self.frequency, self.headers)
+ mod_mock.handle_part.assert_called_once_with(
+ self.data, self.ctype, self.filename, self.payload)
-class TestCmdlineUrl(MockerTestCase):
+class TestCmdlineUrl(unittest.TestCase):
def test_invalid_content(self):
url = "http://example.com/foo"
key = "mykey"
- payload = "0"
+ payload = b"0"
cmdline = "ro %s=%s bar=1" % (key, url)
- mock_readurl = self.mocker.replace(url_helper.readurl,
- passthrough=False)
- mock_readurl(url, ARGS, KWARGS)
- self.mocker.result(url_helper.StringResponse(payload))
- self.mocker.replay()
-
- self.assertEqual((key, url, None),
- util.get_cmdline_url(names=[key], starts="xxxxxx",
- cmdline=cmdline))
+ with mock.patch('cloudinit.url_helper.readurl',
+ return_value=url_helper.StringResponse(payload)):
+ self.assertEqual(
+ util.get_cmdline_url(names=[key], starts="xxxxxx",
+ cmdline=cmdline),
+ (key, url, None))
def test_valid_content(self):
url = "http://example.com/foo"
key = "mykey"
- payload = "xcloud-config\nmydata: foo\nbar: wark\n"
+ payload = b"xcloud-config\nmydata: foo\nbar: wark\n"
cmdline = "ro %s=%s bar=1" % (key, url)
- mock_readurl = self.mocker.replace(url_helper.readurl,
- passthrough=False)
- mock_readurl(url, ARGS, KWARGS)
- self.mocker.result(url_helper.StringResponse(payload))
- self.mocker.replay()
-
- self.assertEqual((key, url, payload),
- util.get_cmdline_url(names=[key], starts="xcloud-config",
- cmdline=cmdline))
+ with mock.patch('cloudinit.url_helper.readurl',
+ return_value=url_helper.StringResponse(payload)):
+ self.assertEqual(
+ util.get_cmdline_url(names=[key], starts=b"xcloud-config",
+ cmdline=cmdline),
+ (key, url, payload))
def test_no_key_found(self):
url = "http://example.com/foo"
key = "mykey"
cmdline = "ro %s=%s bar=1" % (key, url)
- self.mocker.replace(url_helper.readurl, passthrough=False)
- self.mocker.result(url_helper.StringResponse(""))
- self.mocker.replay()
+ with mock.patch('cloudinit.url_helper.readurl',
+ return_value=url_helper.StringResponse(b'')):
+ self.assertEqual(
+ util.get_cmdline_url(names=["does-not-appear"],
+ starts="#cloud-config", cmdline=cmdline),
+ (None, None, None))
- self.assertEqual((None, None, None),
- util.get_cmdline_url(names=["does-not-appear"],
- starts="#cloud-config", cmdline=cmdline))
# vi: ts=4 expandtab
diff --git a/tests/unittests/test_builtin_handlers.py b/tests/unittests/test_builtin_handlers.py
index af7f442e..ad32d0b2 100644
--- a/tests/unittests/test_builtin_handlers.py
+++ b/tests/unittests/test_builtin_handlers.py
@@ -1,6 +1,13 @@
"""Tests of the built-in user data handlers."""
import os
+import shutil
+import tempfile
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
from . import helpers as test_helpers
@@ -14,10 +21,11 @@ from cloudinit.settings import (PER_ALWAYS, PER_INSTANCE)
class TestBuiltins(test_helpers.FilesystemMockingTestCase):
-
def test_upstart_frequency_no_out(self):
- c_root = self.makeDir()
- up_root = self.makeDir()
+ c_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, c_root)
+ up_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, up_root)
paths = helpers.Paths({
'cloud_dir': c_root,
'upstart_dir': up_root,
@@ -36,7 +44,8 @@ class TestBuiltins(test_helpers.FilesystemMockingTestCase):
def test_upstart_frequency_single(self):
# files should be written out when frequency is ! per-instance
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
freq = PER_INSTANCE
self.patchOS(new_root)
@@ -49,16 +58,16 @@ class TestBuiltins(test_helpers.FilesystemMockingTestCase):
util.ensure_dir("/run")
util.ensure_dir("/etc/upstart")
- mock_subp = self.mocker.replace(util.subp, passthrough=False)
- mock_subp(["initctl", "reload-configuration"], capture=False)
- self.mocker.replay()
+ with mock.patch.object(util, 'subp') as mockobj:
+ h = upstart_job.UpstartJobPartHandler(paths)
+ h.handle_part('', handlers.CONTENT_START,
+ None, None, None)
+ h.handle_part('blah', 'text/upstart-job',
+ 'test.conf', 'blah', freq)
+ h.handle_part('', handlers.CONTENT_END,
+ None, None, None)
- h = upstart_job.UpstartJobPartHandler(paths)
- h.handle_part('', handlers.CONTENT_START,
- None, None, None)
- h.handle_part('blah', 'text/upstart-job',
- 'test.conf', 'blah', freq)
- h.handle_part('', handlers.CONTENT_END,
- None, None, None)
+ self.assertEquals(len(os.listdir('/etc/upstart')), 1)
- self.assertEquals(1, len(os.listdir('/etc/upstart')))
+ mockobj.assert_called_once_with(
+ ['initctl', 'reload-configuration'], capture=False)
diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
new file mode 100644
index 00000000..ed863399
--- /dev/null
+++ b/tests/unittests/test_cli.py
@@ -0,0 +1,54 @@
+import imp
+import os
+import sys
+import six
+
+from . import helpers as test_helpers
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+
+
+BIN_CLOUDINIT = "bin/cloud-init"
+
+
+class TestCLI(test_helpers.FilesystemMockingTestCase):
+
+ def setUp(self):
+ super(TestCLI, self).setUp()
+ self.stderr = six.StringIO()
+ self.patchStdoutAndStderr(stderr=self.stderr)
+ self.sys_exit = mock.MagicMock()
+ self.patched_funcs.enter_context(
+ mock.patch.object(sys, 'exit', self.sys_exit))
+
+ def _call_main(self):
+ self.patched_funcs.enter_context(
+ mock.patch.object(sys, 'argv', ['cloud-init']))
+ cli = imp.load_module(
+ 'cli', open(BIN_CLOUDINIT), '', ('', 'r', imp.PY_SOURCE))
+ try:
+ return cli.main()
+ except:
+ pass
+
+ @test_helpers.skipIf(not os.path.isfile(BIN_CLOUDINIT), "no bin/cloudinit")
+ def test_no_arguments_shows_usage(self):
+ self._call_main()
+ self.assertIn('usage: cloud-init', self.stderr.getvalue())
+
+ @test_helpers.skipIf(not os.path.isfile(BIN_CLOUDINIT), "no bin/cloudinit")
+ def test_no_arguments_exits_2(self):
+ exit_code = self._call_main()
+ if self.sys_exit.call_count:
+ self.assertEqual(mock.call(2), self.sys_exit.call_args)
+ else:
+ self.assertEqual(2, exit_code)
+
+ @test_helpers.skipIf(not os.path.isfile(BIN_CLOUDINIT), "no bin/cloudinit")
+ def test_no_arguments_shows_error_message(self):
+ self._call_main()
+ self.assertIn('cloud-init: error: too few arguments',
+ self.stderr.getvalue())
diff --git a/tests/unittests/test_cs_util.py b/tests/unittests/test_cs_util.py
index 7d59222b..d7273035 100644
--- a/tests/unittests/test_cs_util.py
+++ b/tests/unittests/test_cs_util.py
@@ -1,7 +1,21 @@
-from mocker import MockerTestCase
+from __future__ import print_function
+
+import sys
+import unittest
from cloudinit.cs_utils import Cepko
+try:
+ skip = unittest.skip
+except AttributeError:
+ # Python 2.6. Doesn't have to be high fidelity.
+ def skip(reason):
+ def decorator(func):
+ def wrapper(*args, **kws):
+ print(reason, file=sys.stderr)
+ return wrapper
+ return decorator
+
SERVER_CONTEXT = {
"cpu": 1000,
@@ -26,16 +40,21 @@ class CepkoMock(Cepko):
return SERVER_CONTEXT['tags']
-class CepkoResultTests(MockerTestCase):
+# 2015-01-22 BAW: This test is completely useless because it only ever tests
+# the CepkoMock object. Even in its original form, I don't think it ever
+# touched the underlying Cepko class methods.
+@skip('This test is completely useless')
+class CepkoResultTests(unittest.TestCase):
def setUp(self):
- self.mocked = self.mocker.replace("cloudinit.cs_utils.Cepko",
- spec=CepkoMock,
- count=False,
- passthrough=False)
- self.mocked()
- self.mocker.result(CepkoMock())
- self.mocker.replay()
- self.c = Cepko()
+ pass
+ # self.mocked = self.mocker.replace("cloudinit.cs_utils.Cepko",
+ # spec=CepkoMock,
+ # count=False,
+ # passthrough=False)
+ # self.mocked()
+ # self.mocker.result(CepkoMock())
+ # self.mocker.replay()
+ # self.c = Cepko()
def test_getitem(self):
result = self.c.all()
diff --git a/tests/unittests/test_data.py b/tests/unittests/test_data.py
index fd6bd8a1..9c1ec1d4 100644
--- a/tests/unittests/test_data.py
+++ b/tests/unittests/test_data.py
@@ -1,11 +1,19 @@
"""Tests for handling of userdata within cloud init."""
-import StringIO
-
import gzip
import logging
import os
+import shutil
+import tempfile
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+
+from six import BytesIO, StringIO
+from email import encoders
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
@@ -16,13 +24,15 @@ from cloudinit import log
from cloudinit.settings import (PER_INSTANCE)
from cloudinit import sources
from cloudinit import stages
+from cloudinit import user_data as ud
from cloudinit import util
-INSTANCE_ID = "i-testing"
-
from . import helpers
+INSTANCE_ID = "i-testing"
+
+
class FakeDataSource(sources.DataSource):
def __init__(self, userdata=None, vendordata=None):
@@ -32,28 +42,45 @@ class FakeDataSource(sources.DataSource):
self.vendordata_raw = vendordata
+def count_messages(root):
+ am = 0
+ for m in root.walk():
+ if ud.is_skippable(m):
+ continue
+ am += 1
+ return am
+
+
+def gzip_text(text):
+ contents = BytesIO()
+ f = gzip.GzipFile(fileobj=contents, mode='wb')
+ f.write(util.encode_text(text))
+ f.flush()
+ f.close()
+ return contents.getvalue()
+
+
# FIXME: these tests shouldn't be checking log output??
# Weirddddd...
class TestConsumeUserData(helpers.FilesystemMockingTestCase):
def setUp(self):
- helpers.FilesystemMockingTestCase.setUp(self)
+ super(TestConsumeUserData, self).setUp()
self._log = None
self._log_file = None
self._log_handler = None
def tearDown(self):
- helpers.FilesystemMockingTestCase.tearDown(self)
if self._log_handler and self._log:
self._log.removeHandler(self._log_handler)
+ helpers.FilesystemMockingTestCase.tearDown(self)
def _patchIn(self, root):
- self.restore()
self.patchOS(root)
self.patchUtils(root)
def capture_log(self, lvl=logging.DEBUG):
- log_file = StringIO.StringIO()
+ log_file = StringIO()
self._log_handler = logging.StreamHandler(log_file)
self._log_handler.setLevel(lvl)
self._log = log.getLogger()
@@ -71,7 +98,8 @@ class TestConsumeUserData(helpers.FilesystemMockingTestCase):
ci = stages.Init()
ci.datasource = FakeDataSource(blob)
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self.patchUtils(new_root)
self.patchOS(new_root)
ci.fetch()
@@ -99,7 +127,8 @@ class TestConsumeUserData(helpers.FilesystemMockingTestCase):
{ "op": "add", "path": "/foo", "value": "quxC" }
]
'''
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self._patchIn(new_root)
initer = stages.Init()
initer.datasource = FakeDataSource(user_blob, vendordata=vendor_blob)
@@ -138,7 +167,8 @@ class TestConsumeUserData(helpers.FilesystemMockingTestCase):
{ "op": "add", "path": "/foo", "value": "quxC" }
]
'''
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self._patchIn(new_root)
initer = stages.Init()
initer.datasource = FakeDataSource(user_blob, vendordata=vendor_blob)
@@ -184,7 +214,8 @@ c: d
ci = stages.Init()
ci.datasource = FakeDataSource(str(message))
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self.patchUtils(new_root)
self.patchOS(new_root)
ci.fetch()
@@ -214,7 +245,8 @@ name: user
run:
- z
'''
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self._patchIn(new_root)
initer = stages.Init()
initer.datasource = FakeDataSource(user_blob, vendordata=vendor_blob)
@@ -249,7 +281,8 @@ vendor_data:
enabled: True
prefix: /bin/true
'''
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self._patchIn(new_root)
initer = stages.Init()
initer.datasource = FakeDataSource(user_blob, vendordata=vendor_blob)
@@ -309,7 +342,8 @@ p: 1
paths = c_helpers.Paths({}, ds=FakeDataSource(''))
cloud_cfg = handlers.cloud_config.CloudConfigPartHandler(paths)
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self.patchUtils(new_root)
self.patchOS(new_root)
cloud_cfg.handle_part(None, handlers.CONTENT_START, None, None, None,
@@ -335,28 +369,22 @@ p: 1
data = "arbitrary text\n"
ci.datasource = FakeDataSource(data)
- mock_write = self.mocker.replace("cloudinit.util.write_file",
- passthrough=False)
- mock_write(ci.paths.get_ipath("cloud_config"), "", 0600)
- self.mocker.replay()
+ with mock.patch('cloudinit.util.write_file') as mockobj:
+ log_file = self.capture_log(logging.WARNING)
+ ci.fetch()
+ ci.consume_data()
+ self.assertIn(
+ "Unhandled non-multipart (text/x-not-multipart) userdata:",
+ log_file.getvalue())
- log_file = self.capture_log(logging.WARNING)
- ci.fetch()
- ci.consume_data()
- self.assertIn(
- "Unhandled non-multipart (text/x-not-multipart) userdata:",
- log_file.getvalue())
+ mockobj.assert_called_once_with(
+ ci.paths.get_ipath("cloud_config"), "", 0o600)
def test_mime_gzip_compressed(self):
"""Tests that individual message gzip encoding works."""
def gzip_part(text):
- contents = StringIO.StringIO()
- f = gzip.GzipFile(fileobj=contents, mode='w')
- f.write(str(text))
- f.flush()
- f.close()
- return MIMEApplication(contents.getvalue(), 'gzip')
+ return MIMEApplication(gzip_text(text), 'gzip')
base_content1 = '''
#cloud-config
@@ -374,7 +402,8 @@ c: 4
message.attach(gzip_part(base_content2))
ci = stages.Init()
ci.datasource = FakeDataSource(str(message))
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self.patchUtils(new_root)
self.patchOS(new_root)
ci.fetch()
@@ -392,19 +421,17 @@ c: 4
ci = stages.Init()
message = MIMEBase("text", "plain")
message.set_payload("Just text")
- ci.datasource = FakeDataSource(message.as_string())
-
- mock_write = self.mocker.replace("cloudinit.util.write_file",
- passthrough=False)
- mock_write(ci.paths.get_ipath("cloud_config"), "", 0600)
- self.mocker.replay()
-
- log_file = self.capture_log(logging.WARNING)
- ci.fetch()
- ci.consume_data()
- self.assertIn(
- "Unhandled unknown content-type (text/plain)",
- log_file.getvalue())
+ ci.datasource = FakeDataSource(message.as_string().encode())
+
+ with mock.patch('cloudinit.util.write_file') as mockobj:
+ log_file = self.capture_log(logging.WARNING)
+ ci.fetch()
+ ci.consume_data()
+ self.assertIn(
+ "Unhandled unknown content-type (text/plain)",
+ log_file.getvalue())
+ mockobj.assert_called_once_with(
+ ci.paths.get_ipath("cloud_config"), "", 0o600)
def test_shellscript(self):
"""Raw text starting #!/bin/sh is treated as script."""
@@ -413,16 +440,17 @@ c: 4
ci.datasource = FakeDataSource(script)
outpath = os.path.join(ci.paths.get_ipath_cur("scripts"), "part-001")
- mock_write = self.mocker.replace("cloudinit.util.write_file",
- passthrough=False)
- mock_write(ci.paths.get_ipath("cloud_config"), "", 0600)
- mock_write(outpath, script, 0700)
- self.mocker.replay()
- log_file = self.capture_log(logging.WARNING)
- ci.fetch()
- ci.consume_data()
- self.assertEqual("", log_file.getvalue())
+ with mock.patch('cloudinit.util.write_file') as mockobj:
+ log_file = self.capture_log(logging.WARNING)
+ ci.fetch()
+ ci.consume_data()
+ self.assertEqual("", log_file.getvalue())
+
+ mockobj.assert_has_calls([
+ mock.call(outpath, script, 0o700),
+ mock.call(ci.paths.get_ipath("cloud_config"), "", 0o600),
+ ])
def test_mime_text_x_shellscript(self):
"""Mime message of type text/x-shellscript is treated as script."""
@@ -433,16 +461,17 @@ c: 4
ci.datasource = FakeDataSource(message.as_string())
outpath = os.path.join(ci.paths.get_ipath_cur("scripts"), "part-001")
- mock_write = self.mocker.replace("cloudinit.util.write_file",
- passthrough=False)
- mock_write(ci.paths.get_ipath("cloud_config"), "", 0600)
- mock_write(outpath, script, 0700)
- self.mocker.replay()
- log_file = self.capture_log(logging.WARNING)
- ci.fetch()
- ci.consume_data()
- self.assertEqual("", log_file.getvalue())
+ with mock.patch('cloudinit.util.write_file') as mockobj:
+ log_file = self.capture_log(logging.WARNING)
+ ci.fetch()
+ ci.consume_data()
+ self.assertEqual("", log_file.getvalue())
+
+ mockobj.assert_has_calls([
+ mock.call(outpath, script, 0o700),
+ mock.call(ci.paths.get_ipath("cloud_config"), "", 0o600),
+ ])
def test_mime_text_plain_shell(self):
"""Mime type text/plain starting #!/bin/sh is treated as script."""
@@ -453,13 +482,81 @@ c: 4
ci.datasource = FakeDataSource(message.as_string())
outpath = os.path.join(ci.paths.get_ipath_cur("scripts"), "part-001")
- mock_write = self.mocker.replace("cloudinit.util.write_file",
- passthrough=False)
- mock_write(outpath, script, 0700)
- mock_write(ci.paths.get_ipath("cloud_config"), "", 0600)
- self.mocker.replay()
- log_file = self.capture_log(logging.WARNING)
- ci.fetch()
- ci.consume_data()
- self.assertEqual("", log_file.getvalue())
+ with mock.patch('cloudinit.util.write_file') as mockobj:
+ log_file = self.capture_log(logging.WARNING)
+ ci.fetch()
+ ci.consume_data()
+ self.assertEqual("", log_file.getvalue())
+
+ mockobj.assert_has_calls([
+ mock.call(outpath, script, 0o700),
+ mock.call(ci.paths.get_ipath("cloud_config"), "", 0o600),
+ ])
+
+ def test_mime_application_octet_stream(self):
+ """Mime type application/octet-stream is ignored but shows warning."""
+ ci = stages.Init()
+ message = MIMEBase("application", "octet-stream")
+ message.set_payload(b'\xbf\xe6\xb2\xc3\xd3\xba\x13\xa4\xd8\xa1\xcc')
+ encoders.encode_base64(message)
+ ci.datasource = FakeDataSource(message.as_string().encode())
+
+ with mock.patch('cloudinit.util.write_file') as mockobj:
+ log_file = self.capture_log(logging.WARNING)
+ ci.fetch()
+ ci.consume_data()
+ self.assertIn(
+ "Unhandled unknown content-type (application/octet-stream)",
+ log_file.getvalue())
+ mockobj.assert_called_once_with(
+ ci.paths.get_ipath("cloud_config"), "", 0o600)
+
+ def test_cloud_config_archive(self):
+ non_decodable = b'\x11\xc9\xb4gTH\xee\x12'
+ data = [{'content': '#cloud-config\npassword: gocubs\n'},
+ {'content': '#cloud-config\nlocale: chicago\n'},
+ {'content': non_decodable}]
+ message = b'#cloud-config-archive\n' + util.yaml_dumps(data).encode()
+
+ ci = stages.Init()
+ ci.datasource = FakeDataSource(message)
+
+ fs = {}
+
+ def fsstore(filename, content, mode=0o0644, omode="wb"):
+ fs[filename] = content
+
+ # consuming the user-data provided should write 'cloud_config' file
+ # which will have our yaml in it.
+ with mock.patch('cloudinit.util.write_file') as mockobj:
+ mockobj.side_effect = fsstore
+ ci.fetch()
+ ci.consume_data()
+
+ cfg = util.load_yaml(fs[ci.paths.get_ipath("cloud_config")])
+ self.assertEqual(cfg.get('password'), 'gocubs')
+ self.assertEqual(cfg.get('locale'), 'chicago')
+
+
+class TestUDProcess(helpers.ResourceUsingTestCase):
+
+ def test_bytes_in_userdata(self):
+ msg = b'#cloud-config\napt_update: True\n'
+ ud_proc = ud.UserDataProcessor(self.getCloudPaths())
+ message = ud_proc.process(msg)
+ self.assertTrue(count_messages(message) == 1)
+
+ def test_string_in_userdata(self):
+ msg = '#cloud-config\napt_update: True\n'
+
+ ud_proc = ud.UserDataProcessor(self.getCloudPaths())
+ message = ud_proc.process(msg)
+ self.assertTrue(count_messages(message) == 1)
+
+ def test_compressed_in_userdata(self):
+ msg = gzip_text('#cloud-config\napt_update: True\n')
+
+ ud_proc = ud.UserDataProcessor(self.getCloudPaths())
+ message = ud_proc.process(msg)
+ self.assertTrue(count_messages(message) == 1)
diff --git a/tests/unittests/test_datasource/test_altcloud.py b/tests/unittests/test_datasource/test_altcloud.py
index eaaa90e6..85759c68 100644
--- a/tests/unittests/test_datasource/test_altcloud.py
+++ b/tests/unittests/test_datasource/test_altcloud.py
@@ -26,6 +26,7 @@ import shutil
import tempfile
from cloudinit import helpers
+from cloudinit import util
from unittest import TestCase
# Get the cloudinit.sources.DataSourceAltCloud import items needed.
@@ -45,7 +46,7 @@ def _write_cloud_info_file(value):
cifile = open(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 'w')
cifile.write(value)
cifile.close()
- os.chmod(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 0664)
+ os.chmod(cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE, 0o664)
def _remove_cloud_info_file():
@@ -66,12 +67,12 @@ def _write_user_data_files(mount_dir, value):
udfile = open(deltacloud_user_data_file, 'w')
udfile.write(value)
udfile.close()
- os.chmod(deltacloud_user_data_file, 0664)
+ os.chmod(deltacloud_user_data_file, 0o664)
udfile = open(user_data_file, 'w')
udfile.write(value)
udfile.close()
- os.chmod(user_data_file, 0664)
+ os.chmod(user_data_file, 0o664)
def _remove_user_data_files(mount_dir,
@@ -98,6 +99,16 @@ def _remove_user_data_files(mount_dir,
pass
+def _dmi_data(expected):
+ '''
+ Spoof the data received over DMI
+ '''
+ def _data(key):
+ return expected
+
+ return _data
+
+
class TestGetCloudType(TestCase):
'''
Test to exercise method: DataSourceAltCloud.get_cloud_type()
@@ -106,69 +117,42 @@ class TestGetCloudType(TestCase):
def setUp(self):
'''Set up.'''
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
+ self.dmi_data = util.read_dmi_data
# We have a different code path for arm to deal with LP1243287
# We have to switch arch to x86_64 to avoid test failure
force_arch('x86_64')
def tearDown(self):
# Reset
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['dmidecode', '--string', 'system-product-name']
- # Return back to original arch
+ util.read_dmi_data = self.dmi_data
force_arch()
def test_rhev(self):
'''
Test method get_cloud_type() for RHEVm systems.
- Forcing dmidecode return to match a RHEVm system: RHEV Hypervisor
+ Forcing read_dmi_data return to match a RHEVm system: RHEV Hypervisor
'''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['echo', 'RHEV Hypervisor']
+ util.read_dmi_data = _dmi_data('RHEV')
dsrc = DataSourceAltCloud({}, None, self.paths)
- self.assertEquals('RHEV', \
- dsrc.get_cloud_type())
+ self.assertEquals('RHEV', dsrc.get_cloud_type())
def test_vsphere(self):
'''
Test method get_cloud_type() for vSphere systems.
- Forcing dmidecode return to match a vSphere system: RHEV Hypervisor
+ Forcing read_dmi_data return to match a vSphere system: RHEV Hypervisor
'''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['echo', 'VMware Virtual Platform']
+ util.read_dmi_data = _dmi_data('VMware Virtual Platform')
dsrc = DataSourceAltCloud({}, None, self.paths)
- self.assertEquals('VSPHERE', \
- dsrc.get_cloud_type())
+ self.assertEquals('VSPHERE', dsrc.get_cloud_type())
def test_unknown(self):
'''
Test method get_cloud_type() for unknown systems.
- Forcing dmidecode return to match an unrecognized return.
- '''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['echo', 'Unrecognized Platform']
- dsrc = DataSourceAltCloud({}, None, self.paths)
- self.assertEquals('UNKNOWN', \
- dsrc.get_cloud_type())
-
- def test_exception1(self):
- '''
- Test method get_cloud_type() where command dmidecode fails.
- '''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['ls', 'bad command']
- dsrc = DataSourceAltCloud({}, None, self.paths)
- self.assertEquals('UNKNOWN', \
- dsrc.get_cloud_type())
-
- def test_exception2(self):
- '''
- Test method get_cloud_type() where command dmidecode is not available.
+ Forcing read_dmi_data return to match an unrecognized return.
'''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['bad command']
+ util.read_dmi_data = _dmi_data('Unrecognized Platform')
dsrc = DataSourceAltCloud({}, None, self.paths)
- self.assertEquals('UNKNOWN', \
- dsrc.get_cloud_type())
+ self.assertEquals('UNKNOWN', dsrc.get_cloud_type())
class TestGetDataCloudInfoFile(TestCase):
@@ -180,6 +164,7 @@ class TestGetDataCloudInfoFile(TestCase):
'''Set up.'''
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
self.cloud_info_file = tempfile.mkstemp()[1]
+ self.dmi_data = util.read_dmi_data
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
self.cloud_info_file
@@ -192,6 +177,7 @@ class TestGetDataCloudInfoFile(TestCase):
except OSError:
pass
+ util.read_dmi_data = self.dmi_data
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
'/etc/sysconfig/cloud-info'
@@ -243,6 +229,7 @@ class TestGetDataNoCloudInfoFile(TestCase):
def setUp(self):
'''Set up.'''
self.paths = helpers.Paths({'cloud_dir': '/tmp'})
+ self.dmi_data = util.read_dmi_data
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
'no such file'
# We have a different code path for arm to deal with LP1243287
@@ -253,16 +240,14 @@ class TestGetDataNoCloudInfoFile(TestCase):
# Reset
cloudinit.sources.DataSourceAltCloud.CLOUD_INFO_FILE = \
'/etc/sysconfig/cloud-info'
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['dmidecode', '--string', 'system-product-name']
+ util.read_dmi_data = self.dmi_data
# Return back to original arch
force_arch()
def test_rhev_no_cloud_file(self):
'''Test No cloud info file module get_data() forcing RHEV.'''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['echo', 'RHEV Hypervisor']
+ util.read_dmi_data = _dmi_data('RHEV Hypervisor')
dsrc = DataSourceAltCloud({}, None, self.paths)
dsrc.user_data_rhevm = lambda: True
self.assertEquals(True, dsrc.get_data())
@@ -270,8 +255,7 @@ class TestGetDataNoCloudInfoFile(TestCase):
def test_vsphere_no_cloud_file(self):
'''Test No cloud info file module get_data() forcing VSPHERE.'''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['echo', 'VMware Virtual Platform']
+ util.read_dmi_data = _dmi_data('VMware Virtual Platform')
dsrc = DataSourceAltCloud({}, None, self.paths)
dsrc.user_data_vsphere = lambda: True
self.assertEquals(True, dsrc.get_data())
@@ -279,8 +263,7 @@ class TestGetDataNoCloudInfoFile(TestCase):
def test_failure_no_cloud_file(self):
'''Test No cloud info file module get_data() forcing unrecognized.'''
- cloudinit.sources.DataSourceAltCloud.CMD_DMI_SYSTEM = \
- ['echo', 'Unrecognized Platform']
+ util.read_dmi_data = _dmi_data('Unrecognized Platform')
dsrc = DataSourceAltCloud({}, None, self.paths)
self.assertEquals(False, dsrc.get_data())
@@ -426,27 +409,27 @@ class TestReadUserDataCallback(TestCase):
'''Test read_user_data_callback() with both files.'''
self.assertEquals('test user data',
- read_user_data_callback(self.mount_dir))
+ read_user_data_callback(self.mount_dir))
def test_callback_dc(self):
'''Test read_user_data_callback() with only DC file.'''
_remove_user_data_files(self.mount_dir,
- dc_file=False,
- non_dc_file=True)
+ dc_file=False,
+ non_dc_file=True)
self.assertEquals('test user data',
- read_user_data_callback(self.mount_dir))
+ read_user_data_callback(self.mount_dir))
def test_callback_non_dc(self):
'''Test read_user_data_callback() with only non-DC file.'''
_remove_user_data_files(self.mount_dir,
- dc_file=True,
- non_dc_file=False)
+ dc_file=True,
+ non_dc_file=False)
self.assertEquals('test user data',
- read_user_data_callback(self.mount_dir))
+ read_user_data_callback(self.mount_dir))
def test_callback_none(self):
'''Test read_user_data_callback() no files are found.'''
diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
index e992a006..444e2799 100644
--- a/tests/unittests/test_datasource/test_azure.py
+++ b/tests/unittests/test_datasource/test_azure.py
@@ -1,14 +1,24 @@
from cloudinit import helpers
-from cloudinit.util import load_file
+from cloudinit.util import b64e, decode_binary, load_file
from cloudinit.sources import DataSourceAzure
-from ..helpers import populate_dir
+from ..helpers import TestCase, populate_dir
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
-import base64
import crypt
-from mocker import MockerTestCase
import os
import stat
import yaml
+import shutil
+import tempfile
+import xml.etree.ElementTree as ET
def construct_valid_ovf_env(data=None, pubkeys=None, userdata=None):
@@ -40,14 +50,17 @@ def construct_valid_ovf_env(data=None, pubkeys=None, userdata=None):
content += "<%s%s>%s</%s>\n" % (key, attrs, val, key)
if userdata:
- content += "<UserData>%s</UserData>\n" % (base64.b64encode(userdata))
+ content += "<UserData>%s</UserData>\n" % (b64e(userdata))
if pubkeys:
content += "<SSH><PublicKeys>\n"
- for fp, path in pubkeys:
+ for fp, path, value in pubkeys:
content += " <PublicKey>"
- content += ("<Fingerprint>%s</Fingerprint><Path>%s</Path>" %
- (fp, path))
+ if fp and path:
+ content += ("<Fingerprint>%s</Fingerprint><Path>%s</Path>" %
+ (fp, path))
+ if value:
+ content += "<Value>%s</Value>" % value
content += "</PublicKey>\n"
content += "</PublicKeys></SSH>"
content += """
@@ -66,26 +79,25 @@ def construct_valid_ovf_env(data=None, pubkeys=None, userdata=None):
return content
-class TestAzureDataSource(MockerTestCase):
+class TestAzureDataSource(TestCase):
def setUp(self):
- # makeDir comes from MockerTestCase
- self.tmp = self.makeDir()
+ super(TestAzureDataSource, self).setUp()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
# patch cloud_dir, so our 'seed_dir' is guaranteed empty
self.paths = helpers.Paths({'cloud_dir': self.tmp})
self.waagent_d = os.path.join(self.tmp, 'var', 'lib', 'waagent')
- self.unapply = []
- super(TestAzureDataSource, self).setUp()
+ self.patches = ExitStack()
+ self.addCleanup(self.patches.close)
- def tearDown(self):
- apply_patches([i for i in reversed(self.unapply)])
- super(TestAzureDataSource, self).tearDown()
+ super(TestAzureDataSource, self).setUp()
def apply_patches(self, patches):
- ret = apply_patches(patches)
- self.unapply += ret
+ for module, name, new in patches:
+ self.patches.enter_context(mock.patch.object(module, name, new))
def _get_ds(self, data):
@@ -103,13 +115,6 @@ class TestAzureDataSource(MockerTestCase):
data['pubkey_files'] = flist
return ["pubkey_from: %s" % f for f in flist]
- def _iid_from_shared_config(path):
- data['iid_from_shared_cfg'] = path
- return 'i-my-azure-id'
-
- def _apply_hostname_bounce(**kwargs):
- data['apply_hostname_bounce'] = kwargs
-
if data.get('ovfcontent') is not None:
populate_dir(os.path.join(self.paths.seed_dir, "azure"),
{'ovf-env.xml': data['ovfcontent']})
@@ -117,22 +122,63 @@ class TestAzureDataSource(MockerTestCase):
mod = DataSourceAzure
mod.BUILTIN_DS_CONFIG['data_dir'] = self.waagent_d
- self.apply_patches([(mod, 'list_possible_azure_ds_devs', dsdevs)])
-
- self.apply_patches([(mod, 'invoke_agent', _invoke_agent),
- (mod, 'wait_for_files', _wait_for_files),
- (mod, 'pubkeys_from_crt_files',
- _pubkeys_from_crt_files),
- (mod, 'iid_from_shared_config',
- _iid_from_shared_config),
- (mod, 'apply_hostname_bounce',
- _apply_hostname_bounce), ])
+ self.get_metadata_from_fabric = mock.MagicMock(return_value={
+ 'public-keys': [],
+ })
+
+ self.instance_id = 'test-instance-id'
+
+ self.apply_patches([
+ (mod, 'list_possible_azure_ds_devs', dsdevs),
+ (mod, 'invoke_agent', _invoke_agent),
+ (mod, 'wait_for_files', _wait_for_files),
+ (mod, 'pubkeys_from_crt_files', _pubkeys_from_crt_files),
+ (mod, 'perform_hostname_bounce', mock.MagicMock()),
+ (mod, 'get_hostname', mock.MagicMock()),
+ (mod, 'set_hostname', mock.MagicMock()),
+ (mod, 'get_metadata_from_fabric', self.get_metadata_from_fabric),
+ (mod.util, 'read_dmi_data', mock.MagicMock(
+ return_value=self.instance_id)),
+ ])
dsrc = mod.DataSourceAzureNet(
data.get('sys_cfg', {}), distro=None, paths=self.paths)
return dsrc
+ def xml_equals(self, oxml, nxml):
+ """Compare two sets of XML to make sure they are equal"""
+
+ def create_tag_index(xml):
+ et = ET.fromstring(xml)
+ ret = {}
+ for x in et.iter():
+ ret[x.tag] = x
+ return ret
+
+ def tags_exists(x, y):
+ for tag in x.keys():
+ self.assertIn(tag, y)
+ for tag in y.keys():
+ self.assertIn(tag, x)
+
+ def tags_equal(x, y):
+ for x_tag, x_val in x.items():
+ y_val = y.get(x_val.tag)
+ self.assertEquals(x_val.text, y_val.text)
+
+ old_cnt = create_tag_index(oxml)
+ new_cnt = create_tag_index(nxml)
+ tags_exists(old_cnt, new_cnt)
+ tags_equal(old_cnt, new_cnt)
+
+ def xml_notequals(self, oxml, nxml):
+ try:
+ self.xml_equals(oxml, nxml)
+ except AssertionError:
+ return
+ raise AssertionError("XML is the same")
+
def test_basic_seed_dir(self):
odata = {'HostName': "myhost", 'UserName': "myuser"}
data = {'ovfcontent': construct_valid_ovf_env(data=odata),
@@ -145,7 +191,6 @@ class TestAzureDataSource(MockerTestCase):
self.assertEqual(dsrc.metadata['local-hostname'], odata['HostName'])
self.assertTrue(os.path.isfile(
os.path.join(self.waagent_d, 'ovf-env.xml')))
- self.assertEqual(dsrc.metadata['instance-id'], 'i-my-azure-id')
def test_waagent_d_has_0700_perms(self):
# we expect /var/lib/waagent to be created 0700
@@ -153,7 +198,7 @@ class TestAzureDataSource(MockerTestCase):
ret = dsrc.get_data()
self.assertTrue(ret)
self.assertTrue(os.path.isdir(self.waagent_d))
- self.assertEqual(stat.S_IMODE(os.stat(self.waagent_d).st_mode), 0700)
+ self.assertEqual(stat.S_IMODE(os.stat(self.waagent_d).st_mode), 0o700)
def test_user_cfg_set_agent_command_plain(self):
# set dscfg in via plaintext
@@ -162,7 +207,7 @@ class TestAzureDataSource(MockerTestCase):
yaml_cfg = "{agent_command: my_command}\n"
cfg = yaml.safe_load(yaml_cfg)
odata = {'HostName': "myhost", 'UserName': "myuser",
- 'dscfg': {'text': yaml_cfg, 'encoding': 'plain'}}
+ 'dscfg': {'text': yaml_cfg, 'encoding': 'plain'}}
data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
dsrc = self._get_ds(data)
@@ -174,8 +219,8 @@ class TestAzureDataSource(MockerTestCase):
# set dscfg in via base64 encoded yaml
cfg = {'agent_command': "my_command"}
odata = {'HostName': "myhost", 'UserName': "myuser",
- 'dscfg': {'text': base64.b64encode(yaml.dump(cfg)),
- 'encoding': 'base64'}}
+ 'dscfg': {'text': b64e(yaml.dump(cfg)),
+ 'encoding': 'base64'}}
data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
dsrc = self._get_ds(data)
@@ -222,17 +267,28 @@ class TestAzureDataSource(MockerTestCase):
# should equal that after the '$'
pos = defuser['passwd'].rfind("$") + 1
self.assertEqual(defuser['passwd'],
- crypt.crypt(odata['UserPassword'], defuser['passwd'][0:pos]))
+ crypt.crypt(odata['UserPassword'],
+ defuser['passwd'][0:pos]))
+
+ def test_userdata_plain(self):
+ mydata = "FOOBAR"
+ odata = {'UserData': {'text': mydata, 'encoding': 'plain'}}
+ data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
+
+ dsrc = self._get_ds(data)
+ ret = dsrc.get_data()
+ self.assertTrue(ret)
+ self.assertEqual(decode_binary(dsrc.userdata_raw), mydata)
def test_userdata_found(self):
mydata = "FOOBAR"
- odata = {'UserData': base64.b64encode(mydata)}
+ odata = {'UserData': {'text': b64e(mydata), 'encoding': 'base64'}}
data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
dsrc = self._get_ds(data)
ret = dsrc.get_data()
self.assertTrue(ret)
- self.assertEqual(dsrc.userdata_raw, mydata)
+ self.assertEqual(dsrc.userdata_raw, mydata.encode('utf-8'))
def test_no_datasource_expected(self):
# no source should be found if no seed_dir and no devs
@@ -242,10 +298,10 @@ class TestAzureDataSource(MockerTestCase):
self.assertFalse(ret)
self.assertFalse('agent_invoked' in data)
- def test_cfg_has_pubkeys(self):
+ def test_cfg_has_pubkeys_fingerprint(self):
odata = {'HostName': "myhost", 'UserName': "myuser"}
- mypklist = [{'fingerprint': 'fp1', 'path': 'path1'}]
- pubkeys = [(x['fingerprint'], x['path']) for x in mypklist]
+ mypklist = [{'fingerprint': 'fp1', 'path': 'path1', 'value': ''}]
+ pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
data = {'ovfcontent': construct_valid_ovf_env(data=odata,
pubkeys=pubkeys)}
@@ -254,47 +310,38 @@ class TestAzureDataSource(MockerTestCase):
self.assertTrue(ret)
for mypk in mypklist:
self.assertIn(mypk, dsrc.cfg['_pubkeys'])
+ self.assertIn('pubkey_from', dsrc.metadata['public-keys'][-1])
- def test_disabled_bounce(self):
- pass
-
- def test_apply_bounce_call_1(self):
- # hostname needs to get through to apply_hostname_bounce
- odata = {'HostName': 'my-random-hostname'}
- data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
+ def test_cfg_has_pubkeys_value(self):
+ # make sure that provided key is used over fingerprint
+ odata = {'HostName': "myhost", 'UserName': "myuser"}
+ mypklist = [{'fingerprint': 'fp1', 'path': 'path1', 'value': 'value1'}]
+ pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
+ data = {'ovfcontent': construct_valid_ovf_env(data=odata,
+ pubkeys=pubkeys)}
- self._get_ds(data).get_data()
- self.assertIn('hostname', data['apply_hostname_bounce'])
- self.assertEqual(data['apply_hostname_bounce']['hostname'],
- odata['HostName'])
-
- def test_apply_bounce_call_configurable(self):
- # hostname_bounce should be configurable in datasource cfg
- cfg = {'hostname_bounce': {'interface': 'eth1', 'policy': 'off',
- 'command': 'my-bounce-command',
- 'hostname_command': 'my-hostname-command'}}
- odata = {'HostName': "xhost",
- 'dscfg': {'text': base64.b64encode(yaml.dump(cfg)),
- 'encoding': 'base64'}}
- data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
- self._get_ds(data).get_data()
+ dsrc = self._get_ds(data)
+ ret = dsrc.get_data()
+ self.assertTrue(ret)
- for k in cfg['hostname_bounce']:
- self.assertIn(k, data['apply_hostname_bounce'])
+ for mypk in mypklist:
+ self.assertIn(mypk, dsrc.cfg['_pubkeys'])
+ self.assertIn(mypk['value'], dsrc.metadata['public-keys'])
- for k, v in cfg['hostname_bounce'].items():
- self.assertEqual(data['apply_hostname_bounce'][k], v)
+ def test_cfg_has_no_fingerprint_has_value(self):
+ # test value is used when fingerprint not provided
+ odata = {'HostName': "myhost", 'UserName': "myuser"}
+ mypklist = [{'fingerprint': None, 'path': 'path1', 'value': 'value1'}]
+ pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
+ data = {'ovfcontent': construct_valid_ovf_env(data=odata,
+ pubkeys=pubkeys)}
- def test_set_hostname_disabled(self):
- # config specifying set_hostname off should not bounce
- cfg = {'set_hostname': False}
- odata = {'HostName': "xhost",
- 'dscfg': {'text': base64.b64encode(yaml.dump(cfg)),
- 'encoding': 'base64'}}
- data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
- self._get_ds(data).get_data()
+ dsrc = self._get_ds(data)
+ ret = dsrc.get_data()
+ self.assertTrue(ret)
- self.assertEqual(data.get('apply_hostname_bounce', "N/A"), "N/A")
+ for mypk in mypklist:
+ self.assertIn(mypk['value'], dsrc.metadata['public-keys'])
def test_default_ephemeral(self):
# make sure the ephemeral device works
@@ -318,8 +365,8 @@ class TestAzureDataSource(MockerTestCase):
# Make sure that user can affect disk aliases
dscfg = {'disk_aliases': {'ephemeral0': '/dev/sdc'}}
odata = {'HostName': "myhost", 'UserName': "myuser",
- 'dscfg': {'text': base64.b64encode(yaml.dump(dscfg)),
- 'encoding': 'base64'}}
+ 'dscfg': {'text': b64e(yaml.dump(dscfg)),
+ 'encoding': 'base64'}}
usercfg = {'disk_setup': {'/dev/sdc': {'something': '...'},
'ephemeral0': False}}
userdata = '#cloud-config' + yaml.dump(usercfg) + "\n"
@@ -340,7 +387,32 @@ class TestAzureDataSource(MockerTestCase):
dsrc = self._get_ds(data)
dsrc.get_data()
- self.assertEqual(userdata, dsrc.userdata_raw)
+ self.assertEqual(userdata.encode('us-ascii'), dsrc.userdata_raw)
+
+ def test_password_redacted_in_ovf(self):
+ odata = {'HostName': "myhost", 'UserName': "myuser",
+ 'UserPassword': "mypass"}
+ data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
+ dsrc = self._get_ds(data)
+ ret = dsrc.get_data()
+
+ self.assertTrue(ret)
+ ovf_env_path = os.path.join(self.waagent_d, 'ovf-env.xml')
+
+ # The XML should not be same since the user password is redacted
+ on_disk_ovf = load_file(ovf_env_path)
+ self.xml_notequals(data['ovfcontent'], on_disk_ovf)
+
+ # Make sure that the redacted password on disk is not used by CI
+ self.assertNotEquals(dsrc.cfg.get('password'),
+ DataSourceAzure.DEF_PASSWD_REDACTION)
+
+ # Make sure that the password was really encrypted
+ et = ET.fromstring(on_disk_ovf)
+ for elem in et.iter():
+ if 'UserPassword' in elem.tag:
+ self.assertEquals(DataSourceAzure.DEF_PASSWD_REDACTION,
+ elem.text)
def test_ovf_env_arrives_in_waagent_dir(self):
xml = construct_valid_ovf_env(data={}, userdata="FOODATA")
@@ -351,92 +423,224 @@ class TestAzureDataSource(MockerTestCase):
# we expect that the ovf-env.xml file is copied there.
ovf_env_path = os.path.join(self.waagent_d, 'ovf-env.xml')
self.assertTrue(os.path.exists(ovf_env_path))
- self.assertEqual(xml, load_file(ovf_env_path))
-
- def test_existing_ovf_same(self):
- # waagent/SharedConfig left alone if found ovf-env.xml same as cached
- odata = {'UserData': base64.b64encode("SOMEUSERDATA")}
- data = {'ovfcontent': construct_valid_ovf_env(data=odata)}
+ self.xml_equals(xml, load_file(ovf_env_path))
- populate_dir(self.waagent_d,
- {'ovf-env.xml': data['ovfcontent'],
- 'otherfile': 'otherfile-content',
- 'SharedConfig.xml': 'mysharedconfig'})
+ def test_ovf_can_include_unicode(self):
+ xml = construct_valid_ovf_env(data={})
+ xml = u'\ufeff{0}'.format(xml)
+ dsrc = self._get_ds({'ovfcontent': xml})
+ dsrc.get_data()
- dsrc = self._get_ds(data)
- ret = dsrc.get_data()
- self.assertTrue(ret)
- self.assertTrue(os.path.exists(
- os.path.join(self.waagent_d, 'ovf-env.xml')))
- self.assertTrue(os.path.exists(
- os.path.join(self.waagent_d, 'otherfile')))
- self.assertTrue(os.path.exists(
- os.path.join(self.waagent_d, 'SharedConfig.xml')))
-
- def test_existing_ovf_diff(self):
- # waagent/SharedConfig must be removed if ovfenv is found elsewhere
-
- # 'get_data' should remove SharedConfig.xml in /var/lib/waagent
- # if ovf-env.xml differs.
- cached_ovfenv = construct_valid_ovf_env(
- {'userdata': base64.b64encode("FOO_USERDATA")})
- new_ovfenv = construct_valid_ovf_env(
- {'userdata': base64.b64encode("NEW_USERDATA")})
-
- populate_dir(self.waagent_d,
- {'ovf-env.xml': cached_ovfenv,
- 'SharedConfig.xml': "mysharedconfigxml",
- 'otherfile': 'otherfilecontent'})
-
- dsrc = self._get_ds({'ovfcontent': new_ovfenv})
- ret = dsrc.get_data()
+ def test_exception_fetching_fabric_data_doesnt_propagate(self):
+ ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()})
+ ds.ds_cfg['agent_command'] = '__builtin__'
+ self.get_metadata_from_fabric.side_effect = Exception
+ self.assertFalse(ds.get_data())
+
+ def test_fabric_data_included_in_metadata(self):
+ ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()})
+ ds.ds_cfg['agent_command'] = '__builtin__'
+ self.get_metadata_from_fabric.return_value = {'test': 'value'}
+ ret = ds.get_data()
self.assertTrue(ret)
- self.assertEqual(dsrc.userdata_raw, "NEW_USERDATA")
- self.assertTrue(os.path.exists(
- os.path.join(self.waagent_d, 'otherfile')))
- self.assertFalse(
- os.path.exists(os.path.join(self.waagent_d, 'SharedConfig.xml')))
- self.assertTrue(
- os.path.exists(os.path.join(self.waagent_d, 'ovf-env.xml')))
- self.assertEqual(new_ovfenv,
- load_file(os.path.join(self.waagent_d, 'ovf-env.xml')))
-
-
-class TestReadAzureOvf(MockerTestCase):
+ self.assertEqual('value', ds.metadata['test'])
+
+ def test_instance_id_from_dmidecode_used(self):
+ ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()})
+ ds.get_data()
+ self.assertEqual(self.instance_id, ds.metadata['instance-id'])
+
+ def test_instance_id_from_dmidecode_used_for_builtin(self):
+ ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()})
+ ds.ds_cfg['agent_command'] = '__builtin__'
+ ds.get_data()
+ self.assertEqual(self.instance_id, ds.metadata['instance-id'])
+
+
+class TestAzureBounce(TestCase):
+
+ def mock_out_azure_moving_parts(self):
+ self.patches.enter_context(
+ mock.patch.object(DataSourceAzure, 'invoke_agent'))
+ self.patches.enter_context(
+ mock.patch.object(DataSourceAzure, 'wait_for_files'))
+ self.patches.enter_context(
+ mock.patch.object(DataSourceAzure, 'list_possible_azure_ds_devs',
+ mock.MagicMock(return_value=[])))
+ self.patches.enter_context(
+ mock.patch.object(DataSourceAzure,
+ 'find_fabric_formatted_ephemeral_disk',
+ mock.MagicMock(return_value=None)))
+ self.patches.enter_context(
+ mock.patch.object(DataSourceAzure,
+ 'find_fabric_formatted_ephemeral_part',
+ mock.MagicMock(return_value=None)))
+ self.patches.enter_context(
+ mock.patch.object(DataSourceAzure, 'get_metadata_from_fabric',
+ mock.MagicMock(return_value={})))
+ self.patches.enter_context(
+ mock.patch.object(DataSourceAzure.util, 'read_dmi_data',
+ mock.MagicMock(return_value='test-instance-id')))
+
+ def setUp(self):
+ super(TestAzureBounce, self).setUp()
+ self.tmp = tempfile.mkdtemp()
+ self.waagent_d = os.path.join(self.tmp, 'var', 'lib', 'waagent')
+ self.paths = helpers.Paths({'cloud_dir': self.tmp})
+ self.addCleanup(shutil.rmtree, self.tmp)
+ DataSourceAzure.BUILTIN_DS_CONFIG['data_dir'] = self.waagent_d
+ self.patches = ExitStack()
+ self.mock_out_azure_moving_parts()
+ self.get_hostname = self.patches.enter_context(
+ mock.patch.object(DataSourceAzure, 'get_hostname'))
+ self.set_hostname = self.patches.enter_context(
+ mock.patch.object(DataSourceAzure, 'set_hostname'))
+ self.subp = self.patches.enter_context(
+ mock.patch('cloudinit.sources.DataSourceAzure.util.subp'))
+
+ def tearDown(self):
+ self.patches.close()
+
+ def _get_ds(self, ovfcontent=None):
+ if ovfcontent is not None:
+ populate_dir(os.path.join(self.paths.seed_dir, "azure"),
+ {'ovf-env.xml': ovfcontent})
+ return DataSourceAzure.DataSourceAzureNet(
+ {}, distro=None, paths=self.paths)
+
+ def get_ovf_env_with_dscfg(self, hostname, cfg):
+ odata = {
+ 'HostName': hostname,
+ 'dscfg': {
+ 'text': b64e(yaml.dump(cfg)),
+ 'encoding': 'base64'
+ }
+ }
+ return construct_valid_ovf_env(data=odata)
+
+ def test_disabled_bounce_does_not_change_hostname(self):
+ cfg = {'hostname_bounce': {'policy': 'off'}}
+ self._get_ds(self.get_ovf_env_with_dscfg('test-host', cfg)).get_data()
+ self.assertEqual(0, self.set_hostname.call_count)
+
+ @mock.patch('cloudinit.sources.DataSourceAzure.perform_hostname_bounce')
+ def test_disabled_bounce_does_not_perform_bounce(
+ self, perform_hostname_bounce):
+ cfg = {'hostname_bounce': {'policy': 'off'}}
+ self._get_ds(self.get_ovf_env_with_dscfg('test-host', cfg)).get_data()
+ self.assertEqual(0, perform_hostname_bounce.call_count)
+
+ def test_same_hostname_does_not_change_hostname(self):
+ host_name = 'unchanged-host-name'
+ self.get_hostname.return_value = host_name
+ cfg = {'hostname_bounce': {'policy': 'yes'}}
+ self._get_ds(self.get_ovf_env_with_dscfg(host_name, cfg)).get_data()
+ self.assertEqual(0, self.set_hostname.call_count)
+
+ @mock.patch('cloudinit.sources.DataSourceAzure.perform_hostname_bounce')
+ def test_unchanged_hostname_does_not_perform_bounce(
+ self, perform_hostname_bounce):
+ host_name = 'unchanged-host-name'
+ self.get_hostname.return_value = host_name
+ cfg = {'hostname_bounce': {'policy': 'yes'}}
+ self._get_ds(self.get_ovf_env_with_dscfg(host_name, cfg)).get_data()
+ self.assertEqual(0, perform_hostname_bounce.call_count)
+
+ @mock.patch('cloudinit.sources.DataSourceAzure.perform_hostname_bounce')
+ def test_force_performs_bounce_regardless(self, perform_hostname_bounce):
+ host_name = 'unchanged-host-name'
+ self.get_hostname.return_value = host_name
+ cfg = {'hostname_bounce': {'policy': 'force'}}
+ self._get_ds(self.get_ovf_env_with_dscfg(host_name, cfg)).get_data()
+ self.assertEqual(1, perform_hostname_bounce.call_count)
+
+ def test_different_hostnames_sets_hostname(self):
+ expected_hostname = 'azure-expected-host-name'
+ self.get_hostname.return_value = 'default-host-name'
+ self._get_ds(
+ self.get_ovf_env_with_dscfg(expected_hostname, {})).get_data()
+ self.assertEqual(expected_hostname,
+ self.set_hostname.call_args_list[0][0][0])
+
+ @mock.patch('cloudinit.sources.DataSourceAzure.perform_hostname_bounce')
+ def test_different_hostnames_performs_bounce(
+ self, perform_hostname_bounce):
+ expected_hostname = 'azure-expected-host-name'
+ self.get_hostname.return_value = 'default-host-name'
+ self._get_ds(
+ self.get_ovf_env_with_dscfg(expected_hostname, {})).get_data()
+ self.assertEqual(1, perform_hostname_bounce.call_count)
+
+ def test_different_hostnames_sets_hostname_back(self):
+ initial_host_name = 'default-host-name'
+ self.get_hostname.return_value = initial_host_name
+ self._get_ds(
+ self.get_ovf_env_with_dscfg('some-host-name', {})).get_data()
+ self.assertEqual(initial_host_name,
+ self.set_hostname.call_args_list[-1][0][0])
+
+ @mock.patch('cloudinit.sources.DataSourceAzure.perform_hostname_bounce')
+ def test_failure_in_bounce_still_resets_host_name(
+ self, perform_hostname_bounce):
+ perform_hostname_bounce.side_effect = Exception
+ initial_host_name = 'default-host-name'
+ self.get_hostname.return_value = initial_host_name
+ self._get_ds(
+ self.get_ovf_env_with_dscfg('some-host-name', {})).get_data()
+ self.assertEqual(initial_host_name,
+ self.set_hostname.call_args_list[-1][0][0])
+
+ def test_environment_correct_for_bounce_command(self):
+ interface = 'int0'
+ hostname = 'my-new-host'
+ old_hostname = 'my-old-host'
+ self.get_hostname.return_value = old_hostname
+ cfg = {'hostname_bounce': {'interface': interface, 'policy': 'force'}}
+ data = self.get_ovf_env_with_dscfg(hostname, cfg)
+ self._get_ds(data).get_data()
+ self.assertEqual(1, self.subp.call_count)
+ bounce_env = self.subp.call_args[1]['env']
+ self.assertEqual(interface, bounce_env['interface'])
+ self.assertEqual(hostname, bounce_env['hostname'])
+ self.assertEqual(old_hostname, bounce_env['old_hostname'])
+
+ def test_default_bounce_command_used_by_default(self):
+ cmd = 'default-bounce-command'
+ DataSourceAzure.BUILTIN_DS_CONFIG['hostname_bounce']['command'] = cmd
+ cfg = {'hostname_bounce': {'policy': 'force'}}
+ data = self.get_ovf_env_with_dscfg('some-hostname', cfg)
+ self._get_ds(data).get_data()
+ self.assertEqual(1, self.subp.call_count)
+ bounce_args = self.subp.call_args[1]['args']
+ self.assertEqual(cmd, bounce_args)
+
+ @mock.patch('cloudinit.sources.DataSourceAzure.perform_hostname_bounce')
+ def test_set_hostname_option_can_disable_bounce(
+ self, perform_hostname_bounce):
+ cfg = {'set_hostname': False, 'hostname_bounce': {'policy': 'force'}}
+ data = self.get_ovf_env_with_dscfg('some-hostname', cfg)
+ self._get_ds(data).get_data()
+
+ self.assertEqual(0, perform_hostname_bounce.call_count)
+
+ def test_set_hostname_option_can_disable_hostname_set(self):
+ cfg = {'set_hostname': False, 'hostname_bounce': {'policy': 'force'}}
+ data = self.get_ovf_env_with_dscfg('some-hostname', cfg)
+ self._get_ds(data).get_data()
+
+ self.assertEqual(0, self.set_hostname.call_count)
+
+
+class TestReadAzureOvf(TestCase):
def test_invalid_xml_raises_non_azure_ds(self):
invalid_xml = "<foo>" + construct_valid_ovf_env(data={})
self.assertRaises(DataSourceAzure.BrokenAzureDataSource,
- DataSourceAzure.read_azure_ovf, invalid_xml)
+ DataSourceAzure.read_azure_ovf, invalid_xml)
def test_load_with_pubkeys(self):
- mypklist = [{'fingerprint': 'fp1', 'path': 'path1'}]
- pubkeys = [(x['fingerprint'], x['path']) for x in mypklist]
+ mypklist = [{'fingerprint': 'fp1', 'path': 'path1', 'value': ''}]
+ pubkeys = [(x['fingerprint'], x['path'], x['value']) for x in mypklist]
content = construct_valid_ovf_env(pubkeys=pubkeys)
(_md, _ud, cfg) = DataSourceAzure.read_azure_ovf(content)
for mypk in mypklist:
self.assertIn(mypk, cfg['_pubkeys'])
-
-
-class TestReadAzureSharedConfig(MockerTestCase):
- def test_valid_content(self):
- xml = """<?xml version="1.0" encoding="utf-8"?>
- <SharedConfig>
- <Deployment name="MY_INSTANCE_ID">
- <Service name="myservice"/>
- <ServiceInstance name="INSTANCE_ID.0" guid="{abcd-uuid}" />
- </Deployment>
- <Incarnation number="1"/>
- </SharedConfig>"""
- ret = DataSourceAzure.iid_from_shared_config_content(xml)
- self.assertEqual("MY_INSTANCE_ID", ret)
-
-
-def apply_patches(patches):
- ret = []
- for (ref, name, replace) in patches:
- if replace is None:
- continue
- orig = getattr(ref, name)
- setattr(ref, name, replace)
- ret.append((ref, name, orig))
- return ret
diff --git a/tests/unittests/test_datasource/test_azure_helper.py b/tests/unittests/test_datasource/test_azure_helper.py
new file mode 100644
index 00000000..1134199b
--- /dev/null
+++ b/tests/unittests/test_datasource/test_azure_helper.py
@@ -0,0 +1,420 @@
+import os
+
+from cloudinit.sources.helpers import azure as azure_helper
+from ..helpers import TestCase
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
+
+
+GOAL_STATE_TEMPLATE = """\
+<?xml version="1.0" encoding="utf-8"?>
+<GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:noNamespaceSchemaLocation="goalstate10.xsd">
+ <Version>2012-11-30</Version>
+ <Incarnation>{incarnation}</Incarnation>
+ <Machine>
+ <ExpectedState>Started</ExpectedState>
+ <StopRolesDeadlineHint>300000</StopRolesDeadlineHint>
+ <LBProbePorts>
+ <Port>16001</Port>
+ </LBProbePorts>
+ <ExpectHealthReport>FALSE</ExpectHealthReport>
+ </Machine>
+ <Container>
+ <ContainerId>{container_id}</ContainerId>
+ <RoleInstanceList>
+ <RoleInstance>
+ <InstanceId>{instance_id}</InstanceId>
+ <State>Started</State>
+ <Configuration>
+ <HostingEnvironmentConfig>
+ http://100.86.192.70:80/...hostingEnvironmentConfig...
+ </HostingEnvironmentConfig>
+ <SharedConfig>http://100.86.192.70:80/..SharedConfig..</SharedConfig>
+ <ExtensionsConfig>
+ http://100.86.192.70:80/...extensionsConfig...
+ </ExtensionsConfig>
+ <FullConfig>http://100.86.192.70:80/...fullConfig...</FullConfig>
+ <Certificates>{certificates_url}</Certificates>
+ <ConfigName>68ce47.0.68ce47.0.utl-trusty--292258.1.xml</ConfigName>
+ </Configuration>
+ </RoleInstance>
+ </RoleInstanceList>
+ </Container>
+</GoalState>
+"""
+
+
+class TestFindEndpoint(TestCase):
+
+ def setUp(self):
+ super(TestFindEndpoint, self).setUp()
+ patches = ExitStack()
+ self.addCleanup(patches.close)
+
+ self.load_file = patches.enter_context(
+ mock.patch.object(azure_helper.util, 'load_file'))
+
+ def test_missing_file(self):
+ self.load_file.side_effect = IOError
+ self.assertRaises(IOError,
+ azure_helper.WALinuxAgentShim.find_endpoint)
+
+ def test_missing_special_azure_line(self):
+ self.load_file.return_value = ''
+ self.assertRaises(Exception,
+ azure_helper.WALinuxAgentShim.find_endpoint)
+
+ @staticmethod
+ def _build_lease_content(encoded_address):
+ return '\n'.join([
+ 'lease {',
+ ' interface "eth0";',
+ ' option unknown-245 {0};'.format(encoded_address),
+ '}'])
+
+ def test_latest_lease_used(self):
+ encoded_addresses = ['5:4:3:2', '4:3:2:1']
+ file_content = '\n'.join([self._build_lease_content(encoded_address)
+ for encoded_address in encoded_addresses])
+ self.load_file.return_value = file_content
+ self.assertEqual(encoded_addresses[-1].replace(':', '.'),
+ azure_helper.WALinuxAgentShim.find_endpoint())
+
+
+class TestExtractIpAddressFromLeaseValue(TestCase):
+
+ def test_hex_string(self):
+ ip_address, encoded_address = '98.76.54.32', '62:4c:36:20'
+ self.assertEqual(
+ ip_address,
+ azure_helper.WALinuxAgentShim.get_ip_from_lease_value(
+ encoded_address
+ ))
+
+ def test_hex_string_with_single_character_part(self):
+ ip_address, encoded_address = '4.3.2.1', '4:3:2:1'
+ self.assertEqual(
+ ip_address,
+ azure_helper.WALinuxAgentShim.get_ip_from_lease_value(
+ encoded_address
+ ))
+
+ def test_packed_string(self):
+ ip_address, encoded_address = '98.76.54.32', 'bL6 '
+ self.assertEqual(
+ ip_address,
+ azure_helper.WALinuxAgentShim.get_ip_from_lease_value(
+ encoded_address
+ ))
+
+ def test_packed_string_with_escaped_quote(self):
+ ip_address, encoded_address = '100.72.34.108', 'dH\\"l'
+ self.assertEqual(
+ ip_address,
+ azure_helper.WALinuxAgentShim.get_ip_from_lease_value(
+ encoded_address
+ ))
+
+ def test_packed_string_containing_a_colon(self):
+ ip_address, encoded_address = '100.72.58.108', 'dH:l'
+ self.assertEqual(
+ ip_address,
+ azure_helper.WALinuxAgentShim.get_ip_from_lease_value(
+ encoded_address
+ ))
+
+
+class TestGoalStateParsing(TestCase):
+
+ default_parameters = {
+ 'incarnation': 1,
+ 'container_id': 'MyContainerId',
+ 'instance_id': 'MyInstanceId',
+ 'certificates_url': 'MyCertificatesUrl',
+ }
+
+ def _get_goal_state(self, http_client=None, **kwargs):
+ if http_client is None:
+ http_client = mock.MagicMock()
+ parameters = self.default_parameters.copy()
+ parameters.update(kwargs)
+ xml = GOAL_STATE_TEMPLATE.format(**parameters)
+ if parameters['certificates_url'] is None:
+ new_xml_lines = []
+ for line in xml.splitlines():
+ if 'Certificates' in line:
+ continue
+ new_xml_lines.append(line)
+ xml = '\n'.join(new_xml_lines)
+ return azure_helper.GoalState(xml, http_client)
+
+ def test_incarnation_parsed_correctly(self):
+ incarnation = '123'
+ goal_state = self._get_goal_state(incarnation=incarnation)
+ self.assertEqual(incarnation, goal_state.incarnation)
+
+ def test_container_id_parsed_correctly(self):
+ container_id = 'TestContainerId'
+ goal_state = self._get_goal_state(container_id=container_id)
+ self.assertEqual(container_id, goal_state.container_id)
+
+ def test_instance_id_parsed_correctly(self):
+ instance_id = 'TestInstanceId'
+ goal_state = self._get_goal_state(instance_id=instance_id)
+ self.assertEqual(instance_id, goal_state.instance_id)
+
+ def test_certificates_xml_parsed_and_fetched_correctly(self):
+ http_client = mock.MagicMock()
+ certificates_url = 'TestCertificatesUrl'
+ goal_state = self._get_goal_state(
+ http_client=http_client, certificates_url=certificates_url)
+ certificates_xml = goal_state.certificates_xml
+ self.assertEqual(1, http_client.get.call_count)
+ self.assertEqual(certificates_url, http_client.get.call_args[0][0])
+ self.assertTrue(http_client.get.call_args[1].get('secure', False))
+ self.assertEqual(http_client.get.return_value.contents,
+ certificates_xml)
+
+ def test_missing_certificates_skips_http_get(self):
+ http_client = mock.MagicMock()
+ goal_state = self._get_goal_state(
+ http_client=http_client, certificates_url=None)
+ certificates_xml = goal_state.certificates_xml
+ self.assertEqual(0, http_client.get.call_count)
+ self.assertIsNone(certificates_xml)
+
+
+class TestAzureEndpointHttpClient(TestCase):
+
+ regular_headers = {
+ 'x-ms-agent-name': 'WALinuxAgent',
+ 'x-ms-version': '2012-11-30',
+ }
+
+ def setUp(self):
+ super(TestAzureEndpointHttpClient, self).setUp()
+ patches = ExitStack()
+ self.addCleanup(patches.close)
+
+ self.read_file_or_url = patches.enter_context(
+ mock.patch.object(azure_helper.util, 'read_file_or_url'))
+
+ def test_non_secure_get(self):
+ client = azure_helper.AzureEndpointHttpClient(mock.MagicMock())
+ url = 'MyTestUrl'
+ response = client.get(url, secure=False)
+ self.assertEqual(1, self.read_file_or_url.call_count)
+ self.assertEqual(self.read_file_or_url.return_value, response)
+ self.assertEqual(mock.call(url, headers=self.regular_headers),
+ self.read_file_or_url.call_args)
+
+ def test_secure_get(self):
+ url = 'MyTestUrl'
+ certificate = mock.MagicMock()
+ expected_headers = self.regular_headers.copy()
+ expected_headers.update({
+ "x-ms-cipher-name": "DES_EDE3_CBC",
+ "x-ms-guest-agent-public-x509-cert": certificate,
+ })
+ client = azure_helper.AzureEndpointHttpClient(certificate)
+ response = client.get(url, secure=True)
+ self.assertEqual(1, self.read_file_or_url.call_count)
+ self.assertEqual(self.read_file_or_url.return_value, response)
+ self.assertEqual(mock.call(url, headers=expected_headers),
+ self.read_file_or_url.call_args)
+
+ def test_post(self):
+ data = mock.MagicMock()
+ url = 'MyTestUrl'
+ client = azure_helper.AzureEndpointHttpClient(mock.MagicMock())
+ response = client.post(url, data=data)
+ self.assertEqual(1, self.read_file_or_url.call_count)
+ self.assertEqual(self.read_file_or_url.return_value, response)
+ self.assertEqual(
+ mock.call(url, data=data, headers=self.regular_headers),
+ self.read_file_or_url.call_args)
+
+ def test_post_with_extra_headers(self):
+ url = 'MyTestUrl'
+ client = azure_helper.AzureEndpointHttpClient(mock.MagicMock())
+ extra_headers = {'test': 'header'}
+ client.post(url, extra_headers=extra_headers)
+ self.assertEqual(1, self.read_file_or_url.call_count)
+ expected_headers = self.regular_headers.copy()
+ expected_headers.update(extra_headers)
+ self.assertEqual(
+ mock.call(mock.ANY, data=mock.ANY, headers=expected_headers),
+ self.read_file_or_url.call_args)
+
+
+class TestOpenSSLManager(TestCase):
+
+ def setUp(self):
+ super(TestOpenSSLManager, self).setUp()
+ patches = ExitStack()
+ self.addCleanup(patches.close)
+
+ self.subp = patches.enter_context(
+ mock.patch.object(azure_helper.util, 'subp'))
+ try:
+ self.open = patches.enter_context(
+ mock.patch('__builtin__.open'))
+ except ImportError:
+ self.open = patches.enter_context(
+ mock.patch('builtins.open'))
+
+ @mock.patch.object(azure_helper, 'cd', mock.MagicMock())
+ @mock.patch.object(azure_helper.tempfile, 'mkdtemp')
+ def test_openssl_manager_creates_a_tmpdir(self, mkdtemp):
+ manager = azure_helper.OpenSSLManager()
+ self.assertEqual(mkdtemp.return_value, manager.tmpdir)
+
+ def test_generate_certificate_uses_tmpdir(self):
+ subp_directory = {}
+
+ def capture_directory(*args, **kwargs):
+ subp_directory['path'] = os.getcwd()
+
+ self.subp.side_effect = capture_directory
+ manager = azure_helper.OpenSSLManager()
+ self.assertEqual(manager.tmpdir, subp_directory['path'])
+
+ @mock.patch.object(azure_helper, 'cd', mock.MagicMock())
+ @mock.patch.object(azure_helper.tempfile, 'mkdtemp', mock.MagicMock())
+ @mock.patch.object(azure_helper.util, 'del_dir')
+ def test_clean_up(self, del_dir):
+ manager = azure_helper.OpenSSLManager()
+ manager.clean_up()
+ self.assertEqual([mock.call(manager.tmpdir)], del_dir.call_args_list)
+
+
+class TestWALinuxAgentShim(TestCase):
+
+ def setUp(self):
+ super(TestWALinuxAgentShim, self).setUp()
+ patches = ExitStack()
+ self.addCleanup(patches.close)
+
+ self.AzureEndpointHttpClient = patches.enter_context(
+ mock.patch.object(azure_helper, 'AzureEndpointHttpClient'))
+ self.find_endpoint = patches.enter_context(
+ mock.patch.object(
+ azure_helper.WALinuxAgentShim, 'find_endpoint'))
+ self.GoalState = patches.enter_context(
+ mock.patch.object(azure_helper, 'GoalState'))
+ self.OpenSSLManager = patches.enter_context(
+ mock.patch.object(azure_helper, 'OpenSSLManager'))
+ patches.enter_context(
+ mock.patch.object(azure_helper.time, 'sleep', mock.MagicMock()))
+
+ def test_http_client_uses_certificate(self):
+ shim = azure_helper.WALinuxAgentShim()
+ shim.register_with_azure_and_fetch_data()
+ self.assertEqual(
+ [mock.call(self.OpenSSLManager.return_value.certificate)],
+ self.AzureEndpointHttpClient.call_args_list)
+
+ def test_correct_url_used_for_goalstate(self):
+ self.find_endpoint.return_value = 'test_endpoint'
+ shim = azure_helper.WALinuxAgentShim()
+ shim.register_with_azure_and_fetch_data()
+ get = self.AzureEndpointHttpClient.return_value.get
+ self.assertEqual(
+ [mock.call('http://test_endpoint/machine/?comp=goalstate')],
+ get.call_args_list)
+ self.assertEqual(
+ [mock.call(get.return_value.contents,
+ self.AzureEndpointHttpClient.return_value)],
+ self.GoalState.call_args_list)
+
+ def test_certificates_used_to_determine_public_keys(self):
+ shim = azure_helper.WALinuxAgentShim()
+ data = shim.register_with_azure_and_fetch_data()
+ self.assertEqual(
+ [mock.call(self.GoalState.return_value.certificates_xml)],
+ self.OpenSSLManager.return_value.parse_certificates.call_args_list)
+ self.assertEqual(
+ self.OpenSSLManager.return_value.parse_certificates.return_value,
+ data['public-keys'])
+
+ def test_absent_certificates_produces_empty_public_keys(self):
+ self.GoalState.return_value.certificates_xml = None
+ shim = azure_helper.WALinuxAgentShim()
+ data = shim.register_with_azure_and_fetch_data()
+ self.assertEqual([], data['public-keys'])
+
+ def test_correct_url_used_for_report_ready(self):
+ self.find_endpoint.return_value = 'test_endpoint'
+ shim = azure_helper.WALinuxAgentShim()
+ shim.register_with_azure_and_fetch_data()
+ expected_url = 'http://test_endpoint/machine?comp=health'
+ self.assertEqual(
+ [mock.call(expected_url, data=mock.ANY, extra_headers=mock.ANY)],
+ self.AzureEndpointHttpClient.return_value.post.call_args_list)
+
+ def test_goal_state_values_used_for_report_ready(self):
+ self.GoalState.return_value.incarnation = 'TestIncarnation'
+ self.GoalState.return_value.container_id = 'TestContainerId'
+ self.GoalState.return_value.instance_id = 'TestInstanceId'
+ shim = azure_helper.WALinuxAgentShim()
+ shim.register_with_azure_and_fetch_data()
+ posted_document = (
+ self.AzureEndpointHttpClient.return_value.post.call_args[1]['data']
+ )
+ self.assertIn('TestIncarnation', posted_document)
+ self.assertIn('TestContainerId', posted_document)
+ self.assertIn('TestInstanceId', posted_document)
+
+ def test_clean_up_can_be_called_at_any_time(self):
+ shim = azure_helper.WALinuxAgentShim()
+ shim.clean_up()
+
+ def test_clean_up_will_clean_up_openssl_manager_if_instantiated(self):
+ shim = azure_helper.WALinuxAgentShim()
+ shim.register_with_azure_and_fetch_data()
+ shim.clean_up()
+ self.assertEqual(
+ 1, self.OpenSSLManager.return_value.clean_up.call_count)
+
+ def test_failure_to_fetch_goalstate_bubbles_up(self):
+ class SentinelException(Exception):
+ pass
+ self.AzureEndpointHttpClient.return_value.get.side_effect = (
+ SentinelException)
+ shim = azure_helper.WALinuxAgentShim()
+ self.assertRaises(SentinelException,
+ shim.register_with_azure_and_fetch_data)
+
+
+class TestGetMetadataFromFabric(TestCase):
+
+ @mock.patch.object(azure_helper, 'WALinuxAgentShim')
+ def test_data_from_shim_returned(self, shim):
+ ret = azure_helper.get_metadata_from_fabric()
+ self.assertEqual(
+ shim.return_value.register_with_azure_and_fetch_data.return_value,
+ ret)
+
+ @mock.patch.object(azure_helper, 'WALinuxAgentShim')
+ def test_success_calls_clean_up(self, shim):
+ azure_helper.get_metadata_from_fabric()
+ self.assertEqual(1, shim.return_value.clean_up.call_count)
+
+ @mock.patch.object(azure_helper, 'WALinuxAgentShim')
+ def test_failure_in_registration_calls_clean_up(self, shim):
+ class SentinelException(Exception):
+ pass
+ shim.return_value.register_with_azure_and_fetch_data.side_effect = (
+ SentinelException)
+ self.assertRaises(SentinelException,
+ azure_helper.get_metadata_from_fabric)
+ self.assertEqual(1, shim.return_value.clean_up.call_count)
diff --git a/tests/unittests/test_datasource/test_cloudsigma.py b/tests/unittests/test_datasource/test_cloudsigma.py
index 306ac7d8..772d189a 100644
--- a/tests/unittests/test_datasource/test_cloudsigma.py
+++ b/tests/unittests/test_datasource/test_cloudsigma.py
@@ -39,6 +39,7 @@ class CepkoMock(Cepko):
class DataSourceCloudSigmaTest(test_helpers.TestCase):
def setUp(self):
+ super(DataSourceCloudSigmaTest, self).setUp()
self.datasource = DataSourceCloudSigma.DataSourceCloudSigma("", "", "")
self.datasource.is_running_in_cloudsigma = lambda: True
self.datasource.cepko = CepkoMock(SERVER_CONTEXT)
diff --git a/tests/unittests/test_datasource/test_cloudstack.py b/tests/unittests/test_datasource/test_cloudstack.py
new file mode 100644
index 00000000..656d80d1
--- /dev/null
+++ b/tests/unittests/test_datasource/test_cloudstack.py
@@ -0,0 +1,86 @@
+from cloudinit import helpers
+from cloudinit.sources.DataSourceCloudStack import DataSourceCloudStack
+from ..helpers import TestCase
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
+
+
+class TestCloudStackPasswordFetching(TestCase):
+
+ def setUp(self):
+ super(TestCloudStackPasswordFetching, self).setUp()
+ self.patches = ExitStack()
+ self.addCleanup(self.patches.close)
+ mod_name = 'cloudinit.sources.DataSourceCloudStack'
+ self.patches.enter_context(mock.patch('{0}.ec2'.format(mod_name)))
+ self.patches.enter_context(mock.patch('{0}.uhelp'.format(mod_name)))
+
+ def _set_password_server_response(self, response_string):
+ subp = mock.MagicMock(return_value=(response_string, ''))
+ self.patches.enter_context(
+ mock.patch('cloudinit.sources.DataSourceCloudStack.util.subp',
+ subp))
+ return subp
+
+ def test_empty_password_doesnt_create_config(self):
+ self._set_password_server_response('')
+ ds = DataSourceCloudStack({}, None, helpers.Paths({}))
+ ds.get_data()
+ self.assertEqual({}, ds.get_config_obj())
+
+ def test_saved_password_doesnt_create_config(self):
+ self._set_password_server_response('saved_password')
+ ds = DataSourceCloudStack({}, None, helpers.Paths({}))
+ ds.get_data()
+ self.assertEqual({}, ds.get_config_obj())
+
+ def test_password_sets_password(self):
+ password = 'SekritSquirrel'
+ self._set_password_server_response(password)
+ ds = DataSourceCloudStack({}, None, helpers.Paths({}))
+ ds.get_data()
+ self.assertEqual(password, ds.get_config_obj()['password'])
+
+ def test_bad_request_doesnt_stop_ds_from_working(self):
+ self._set_password_server_response('bad_request')
+ ds = DataSourceCloudStack({}, None, helpers.Paths({}))
+ self.assertTrue(ds.get_data())
+
+ def assertRequestTypesSent(self, subp, expected_request_types):
+ request_types = []
+ for call in subp.call_args_list:
+ args = call[0][0]
+ for arg in args:
+ if arg.startswith('DomU_Request'):
+ request_types.append(arg.split()[1])
+ self.assertEqual(expected_request_types, request_types)
+
+ def test_valid_response_means_password_marked_as_saved(self):
+ password = 'SekritSquirrel'
+ subp = self._set_password_server_response(password)
+ ds = DataSourceCloudStack({}, None, helpers.Paths({}))
+ ds.get_data()
+ self.assertRequestTypesSent(subp,
+ ['send_my_password', 'saved_password'])
+
+ def _check_password_not_saved_for(self, response_string):
+ subp = self._set_password_server_response(response_string)
+ ds = DataSourceCloudStack({}, None, helpers.Paths({}))
+ ds.get_data()
+ self.assertRequestTypesSent(subp, ['send_my_password'])
+
+ def test_password_not_saved_if_empty(self):
+ self._check_password_not_saved_for('')
+
+ def test_password_not_saved_if_already_saved(self):
+ self._check_password_not_saved_for('saved_password')
+
+ def test_password_not_saved_if_bad_request(self):
+ self._check_password_not_saved_for('bad_request')
diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py
index d88066e5..89b15f54 100644
--- a/tests/unittests/test_datasource/test_configdrive.py
+++ b/tests/unittests/test_datasource/test_configdrive.py
@@ -1,10 +1,18 @@
from copy import copy
import json
import os
-import os.path
-
-import mocker
-from mocker import MockerTestCase
+import shutil
+import six
+import tempfile
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
from cloudinit import helpers
from cloudinit import settings
@@ -12,7 +20,8 @@ from cloudinit.sources import DataSourceConfigDrive as ds
from cloudinit.sources.helpers import openstack
from cloudinit import util
-from .. import helpers as unit_helpers
+from ..helpers import TestCase
+
PUBKEY = u'ssh-rsa AAAAB3NzaC1....sIkJhq8wdX+4I3A4cYbYP ubuntu@server-460\n'
EC2_META = {
@@ -37,7 +46,7 @@ EC2_META = {
'reservation-id': 'r-iru5qm4m',
'security-groups': ['default']
}
-USER_DATA = '#!/bin/sh\necho This is user data\n'
+USER_DATA = b'#!/bin/sh\necho This is user data\n'
OSTACK_META = {
'availability_zone': 'nova',
'files': [{'content_path': '/content/0000', 'path': '/etc/foo.cfg'},
@@ -48,27 +57,60 @@ OSTACK_META = {
'public_keys': {'mykey': PUBKEY},
'uuid': 'b0fa911b-69d4-4476-bbe2-1c92bff6535c'}
-CONTENT_0 = 'This is contents of /etc/foo.cfg\n'
-CONTENT_1 = '# this is /etc/bar/bar.cfg\n'
+CONTENT_0 = b'This is contents of /etc/foo.cfg\n'
+CONTENT_1 = b'# this is /etc/bar/bar.cfg\n'
+NETWORK_DATA = {
+ 'services': [
+ {'type': 'dns', 'address': '199.204.44.24'},
+ {'type': 'dns', 'address': '199.204.47.54'}
+ ],
+ 'links': [
+ {'vif_id': '2ecc7709-b3f7-4448-9580-e1ec32d75bbd',
+ 'ethernet_mac_address': 'fa:16:3e:69:b0:58',
+ 'type': 'ovs', 'mtu': None, 'id': 'tap2ecc7709-b3'},
+ {'vif_id': '2f88d109-5b57-40e6-af32-2472df09dc33',
+ 'ethernet_mac_address': 'fa:16:3e:d4:57:ad',
+ 'type': 'ovs', 'mtu': None, 'id': 'tap2f88d109-5b'},
+ {'vif_id': '1a5382f8-04c5-4d75-ab98-d666c1ef52cc',
+ 'ethernet_mac_address': 'fa:16:3e:05:30:fe',
+ 'type': 'ovs', 'mtu': None, 'id': 'tap1a5382f8-04'}
+ ],
+ 'networks': [
+ {'link': 'tap2ecc7709-b3', 'type': 'ipv4_dhcp',
+ 'network_id': '6d6357ac-0f70-4afa-8bd7-c274cc4ea235',
+ 'id': 'network0'},
+ {'link': 'tap2f88d109-5b', 'type': 'ipv4_dhcp',
+ 'network_id': 'd227a9b3-6960-4d94-8976-ee5788b44f54',
+ 'id': 'network1'},
+ {'link': 'tap1a5382f8-04', 'type': 'ipv4_dhcp',
+ 'network_id': 'dab2ba57-cae2-4311-a5ed-010b263891f5',
+ 'id': 'network2'}
+ ]
+}
CFG_DRIVE_FILES_V2 = {
- 'ec2/2009-04-04/meta-data.json': json.dumps(EC2_META),
- 'ec2/2009-04-04/user-data': USER_DATA,
- 'ec2/latest/meta-data.json': json.dumps(EC2_META),
- 'ec2/latest/user-data': USER_DATA,
- 'openstack/2012-08-10/meta_data.json': json.dumps(OSTACK_META),
- 'openstack/2012-08-10/user_data': USER_DATA,
- 'openstack/content/0000': CONTENT_0,
- 'openstack/content/0001': CONTENT_1,
- 'openstack/latest/meta_data.json': json.dumps(OSTACK_META),
- 'openstack/latest/user_data': USER_DATA}
-
-
-class TestConfigDriveDataSource(MockerTestCase):
+ 'ec2/2009-04-04/meta-data.json': json.dumps(EC2_META),
+ 'ec2/2009-04-04/user-data': USER_DATA,
+ 'ec2/latest/meta-data.json': json.dumps(EC2_META),
+ 'ec2/latest/user-data': USER_DATA,
+ 'openstack/2012-08-10/meta_data.json': json.dumps(OSTACK_META),
+ 'openstack/2012-08-10/user_data': USER_DATA,
+ 'openstack/content/0000': CONTENT_0,
+ 'openstack/content/0001': CONTENT_1,
+ 'openstack/latest/meta_data.json': json.dumps(OSTACK_META),
+ 'openstack/latest/user_data': USER_DATA,
+ 'openstack/latest/network_data.json': json.dumps(NETWORK_DATA),
+ 'openstack/2015-10-15/meta_data.json': json.dumps(OSTACK_META),
+ 'openstack/2015-10-15/user_data': USER_DATA,
+ 'openstack/2015-10-15/network_data.json': json.dumps(NETWORK_DATA)}
+
+
+class TestConfigDriveDataSource(TestCase):
def setUp(self):
super(TestConfigDriveDataSource, self).setUp()
- self.tmp = self.makeDir()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def test_ec2_metadata(self):
populate_dir(self.tmp, CFG_DRIVE_FILES_V2)
@@ -91,23 +133,29 @@ class TestConfigDriveDataSource(MockerTestCase):
'swap': '/dev/vda3',
}
for name, dev_name in name_tests.items():
- with unit_helpers.mocker() as my_mock:
- find_mock = my_mock.replace(util.find_devs_with,
- spec=False, passthrough=False)
+ with ExitStack() as mocks:
provided_name = dev_name[len('/dev/'):]
provided_name = "s" + provided_name[1:]
- find_mock(mocker.ARGS)
- my_mock.result([provided_name])
- exists_mock = my_mock.replace(os.path.exists,
- spec=False, passthrough=False)
- exists_mock(mocker.ARGS)
- my_mock.result(False)
- exists_mock(mocker.ARGS)
- my_mock.result(True)
- my_mock.replay()
+ find_mock = mocks.enter_context(
+ mock.patch.object(util, 'find_devs_with',
+ return_value=[provided_name]))
+ # We want os.path.exists() to return False on its first call,
+ # and True on its second call. We use a handy generator as
+ # the mock side effect for this. The mocked function returns
+ # what the side effect returns.
+
+ def exists_side_effect():
+ yield False
+ yield True
+ exists_mock = mocks.enter_context(
+ mock.patch.object(os.path, 'exists',
+ side_effect=exists_side_effect()))
device = cfg_ds.device_name_to_device(name)
self.assertEquals(dev_name, device)
+ find_mock.assert_called_once_with(mock.ANY)
+ self.assertEqual(exists_mock.call_count, 2)
+
def test_dev_os_map(self):
populate_dir(self.tmp, CFG_DRIVE_FILES_V2)
cfg_ds = ds.DataSourceConfigDrive(settings.CFG_BUILTIN,
@@ -123,19 +171,19 @@ class TestConfigDriveDataSource(MockerTestCase):
'swap': '/dev/vda3',
}
for name, dev_name in name_tests.items():
- with unit_helpers.mocker() as my_mock:
- find_mock = my_mock.replace(util.find_devs_with,
- spec=False, passthrough=False)
- find_mock(mocker.ARGS)
- my_mock.result([dev_name])
- exists_mock = my_mock.replace(os.path.exists,
- spec=False, passthrough=False)
- exists_mock(mocker.ARGS)
- my_mock.result(True)
- my_mock.replay()
+ with ExitStack() as mocks:
+ find_mock = mocks.enter_context(
+ mock.patch.object(util, 'find_devs_with',
+ return_value=[dev_name]))
+ exists_mock = mocks.enter_context(
+ mock.patch.object(os.path, 'exists',
+ return_value=True))
device = cfg_ds.device_name_to_device(name)
self.assertEquals(dev_name, device)
+ find_mock.assert_called_once_with(mock.ANY)
+ exists_mock.assert_called_once_with(mock.ANY)
+
def test_dev_ec2_remap(self):
populate_dir(self.tmp, CFG_DRIVE_FILES_V2)
cfg_ds = ds.DataSourceConfigDrive(settings.CFG_BUILTIN,
@@ -156,16 +204,21 @@ class TestConfigDriveDataSource(MockerTestCase):
'root2k': None,
}
for name, dev_name in name_tests.items():
- with unit_helpers.mocker(verify_calls=False) as my_mock:
- exists_mock = my_mock.replace(os.path.exists,
- spec=False, passthrough=False)
- exists_mock(mocker.ARGS)
- my_mock.result(False)
- exists_mock(mocker.ARGS)
- my_mock.result(True)
- my_mock.replay()
+ # We want os.path.exists() to return False on its first call,
+ # and True on its second call. We use a handy generator as
+ # the mock side effect for this. The mocked function returns
+ # what the side effect returns.
+ def exists_side_effect():
+ yield False
+ yield True
+ with mock.patch.object(os.path, 'exists',
+ side_effect=exists_side_effect()):
device = cfg_ds.device_name_to_device(name)
self.assertEquals(dev_name, device)
+ # We don't assert the call count for os.path.exists() because
+ # not all of the entries in name_tests results in two calls to
+ # that function. Specifically, 'root2k' doesn't seem to call
+ # it at all.
def test_dev_ec2_map(self):
populate_dir(self.tmp, CFG_DRIVE_FILES_V2)
@@ -173,12 +226,6 @@ class TestConfigDriveDataSource(MockerTestCase):
None,
helpers.Paths({}))
found = ds.read_config_drive(self.tmp)
- exists_mock = self.mocker.replace(os.path.exists,
- spec=False, passthrough=False)
- exists_mock(mocker.ARGS)
- self.mocker.count(0, None)
- self.mocker.result(True)
- self.mocker.replay()
ec2_md = found['ec2-metadata']
os_md = found['metadata']
cfg_ds.ec2_metadata = ec2_md
@@ -193,8 +240,9 @@ class TestConfigDriveDataSource(MockerTestCase):
'root2k': None,
}
for name, dev_name in name_tests.items():
- device = cfg_ds.device_name_to_device(name)
- self.assertEquals(dev_name, device)
+ with mock.patch.object(os.path, 'exists', return_value=True):
+ device = cfg_ds.device_name_to_device(name)
+ self.assertEquals(dev_name, device)
def test_dir_valid(self):
"""Verify a dir is read as such."""
@@ -209,6 +257,7 @@ class TestConfigDriveDataSource(MockerTestCase):
self.assertEqual(USER_DATA, found['userdata'])
self.assertEqual(expected_md, found['metadata'])
+ self.assertEqual(NETWORK_DATA, found['networkdata'])
self.assertEqual(found['files']['/etc/foo.cfg'], CONTENT_0)
self.assertEqual(found['files']['/etc/bar/bar.cfg'], CONTENT_1)
@@ -234,6 +283,7 @@ class TestConfigDriveDataSource(MockerTestCase):
data = copy(CFG_DRIVE_FILES_V2)
data["openstack/2012-08-10/meta_data.json"] = "non-json garbage {}"
+ data["openstack/2015-10-15/meta_data.json"] = "non-json garbage {}"
data["openstack/latest/meta_data.json"] = "non-json garbage {}"
populate_dir(self.tmp, data)
@@ -277,9 +327,8 @@ class TestConfigDriveDataSource(MockerTestCase):
util.is_partition = my_is_partition
devs_with_answers = {"TYPE=vfat": [],
- "TYPE=iso9660": ["/dev/vdb"],
- "LABEL=config-2": ["/dev/vdb"],
- }
+ "TYPE=iso9660": ["/dev/vdb"],
+ "LABEL=config-2": ["/dev/vdb"]}
self.assertEqual(["/dev/vdb"], ds.find_candidate_devs())
# add a vfat item
@@ -290,9 +339,10 @@ class TestConfigDriveDataSource(MockerTestCase):
# verify that partitions are considered, that have correct label.
devs_with_answers = {"TYPE=vfat": ["/dev/sda1"],
- "TYPE=iso9660": [], "LABEL=config-2": ["/dev/vdb3"]}
+ "TYPE=iso9660": [],
+ "LABEL=config-2": ["/dev/vdb3"]}
self.assertEqual(["/dev/vdb3"],
- ds.find_candidate_devs())
+ ds.find_candidate_devs())
finally:
util.find_devs_with = orig_find_devs_with
@@ -303,7 +353,20 @@ class TestConfigDriveDataSource(MockerTestCase):
populate_dir(self.tmp, CFG_DRIVE_FILES_V2)
myds = cfg_ds_from_dir(self.tmp)
self.assertEqual(myds.get_public_ssh_keys(),
- [OSTACK_META['public_keys']['mykey']])
+ [OSTACK_META['public_keys']['mykey']])
+
+ def test_network_data_is_found(self):
+ """Verify that network_data is present in ds in config-drive-v2."""
+ populate_dir(self.tmp, CFG_DRIVE_FILES_V2)
+ myds = cfg_ds_from_dir(self.tmp)
+ self.assertEqual(myds.network_json, NETWORK_DATA)
+
+ def test_network_config_is_converted(self):
+ """Verify that network_data is converted and present on ds object."""
+ populate_dir(self.tmp, CFG_DRIVE_FILES_V2)
+ myds = cfg_ds_from_dir(self.tmp)
+ network_config = ds.convert_network_data(NETWORK_DATA)
+ self.assertEqual(myds.network_config, network_config)
def cfg_ds_from_dir(seed_d):
@@ -323,16 +386,22 @@ def populate_ds_from_read_config(cfg_ds, source, results):
cfg_ds.ec2_metadata = results.get('ec2-metadata')
cfg_ds.userdata_raw = results.get('userdata')
cfg_ds.version = results.get('version')
+ cfg_ds.network_json = results.get('networkdata')
+ cfg_ds._network_config = ds.convert_network_data(cfg_ds.network_json)
def populate_dir(seed_dir, files):
- for (name, content) in files.iteritems():
+ for (name, content) in files.items():
path = os.path.join(seed_dir, name)
dirname = os.path.dirname(path)
if not os.path.isdir(dirname):
os.makedirs(dirname)
- with open(path, "w") as fp:
+ if isinstance(content, six.text_type):
+ mode = "w"
+ else:
+ mode = "wb"
+
+ with open(path, mode) as fp:
fp.write(content)
- fp.close()
# vi: ts=4 expandtab
diff --git a/tests/unittests/test_datasource/test_digitalocean.py b/tests/unittests/test_datasource/test_digitalocean.py
index d1270fc2..679d1b82 100644
--- a/tests/unittests/test_datasource/test_digitalocean.py
+++ b/tests/unittests/test_datasource/test_digitalocean.py
@@ -15,11 +15,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import httpretty
import re
-from types import ListType
-from urlparse import urlparse
+from six.moves.urllib_parse import urlparse
from cloudinit import settings
from cloudinit import helpers
@@ -27,6 +25,8 @@ from cloudinit.sources import DataSourceDigitalOcean
from .. import helpers as test_helpers
+httpretty = test_helpers.import_httpretty()
+
# Abbreviated for the test
DO_INDEX = """id
hostname
@@ -110,7 +110,7 @@ class TestDataSourceDigitalOcean(test_helpers.HttprettyTestCase):
self.assertEqual([DO_META.get('public-keys')],
self.ds.get_public_ssh_keys())
- self.assertIs(type(self.ds.get_public_ssh_keys()), ListType)
+ self.assertIsInstance(self.ds.get_public_ssh_keys(), list)
@httpretty.activate
def test_multiple_ssh_keys(self):
@@ -124,4 +124,4 @@ class TestDataSourceDigitalOcean(test_helpers.HttprettyTestCase):
self.assertEqual(DO_META.get('public-keys').splitlines(),
self.ds.get_public_ssh_keys())
- self.assertIs(type(self.ds.get_public_ssh_keys()), ListType)
+ self.assertIsInstance(self.ds.get_public_ssh_keys(), list)
diff --git a/tests/unittests/test_datasource/test_gce.py b/tests/unittests/test_datasource/test_gce.py
index 06050bb1..fa714070 100644
--- a/tests/unittests/test_datasource/test_gce.py
+++ b/tests/unittests/test_datasource/test_gce.py
@@ -15,11 +15,10 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import httpretty
import re
from base64 import b64encode, b64decode
-from urlparse import urlparse
+from six.moves.urllib_parse import urlparse
from cloudinit import settings
from cloudinit import helpers
@@ -27,12 +26,14 @@ from cloudinit.sources import DataSourceGCE
from .. import helpers as test_helpers
+httpretty = test_helpers.import_httpretty()
+
GCE_META = {
'instance/id': '123',
'instance/zone': 'foo/bar',
'project/attributes/sshKeys': 'user:ssh-rsa AA2..+aRD0fyVw== root@server',
'instance/hostname': 'server.project-foo.local',
- 'instance/attributes/user-data': '/bin/echo foo\n',
+ 'instance/attributes/user-data': b'/bin/echo foo\n',
}
GCE_META_PARTIAL = {
@@ -45,7 +46,7 @@ GCE_META_ENCODING = {
'instance/id': '12345',
'instance/hostname': 'server.project-baz.local',
'instance/zone': 'baz/bang',
- 'instance/attributes/user-data': b64encode('/bin/echo baz\n'),
+ 'instance/attributes/user-data': b64encode(b'/bin/echo baz\n'),
'instance/attributes/user-data-encoding': 'base64',
}
@@ -54,8 +55,8 @@ MD_URL_RE = re.compile(
r'http://metadata.google.internal./computeMetadata/v1/.*')
-def _new_request_callback(gce_meta=None):
- if not gce_meta:
+def _set_mock_metadata(gce_meta=None):
+ if gce_meta is None:
gce_meta = GCE_META
def _request_callback(method, uri, headers):
@@ -69,9 +70,10 @@ def _new_request_callback(gce_meta=None):
else:
return (404, headers, '')
- return _request_callback
+ httpretty.register_uri(httpretty.GET, MD_URL_RE, body=_request_callback)
+@httpretty.activate
class TestDataSourceGCE(test_helpers.HttprettyTestCase):
def setUp(self):
@@ -80,23 +82,16 @@ class TestDataSourceGCE(test_helpers.HttprettyTestCase):
helpers.Paths({}))
super(TestDataSourceGCE, self).setUp()
- @httpretty.activate
def test_connection(self):
- httpretty.register_uri(
- httpretty.GET, MD_URL_RE,
- body=_new_request_callback())
-
+ _set_mock_metadata()
success = self.ds.get_data()
self.assertTrue(success)
req_header = httpretty.last_request().headers
self.assertDictContainsSubset(HEADERS, req_header)
- @httpretty.activate
def test_metadata(self):
- httpretty.register_uri(
- httpretty.GET, MD_URL_RE,
- body=_new_request_callback())
+ _set_mock_metadata()
self.ds.get_data()
shostname = GCE_META.get('instance/hostname').split('.')[0]
@@ -106,22 +101,12 @@ class TestDataSourceGCE(test_helpers.HttprettyTestCase):
self.assertEqual(GCE_META.get('instance/id'),
self.ds.get_instance_id())
- self.assertEqual(GCE_META.get('instance/zone'),
- self.ds.availability_zone)
-
self.assertEqual(GCE_META.get('instance/attributes/user-data'),
self.ds.get_userdata_raw())
- # we expect a list of public ssh keys with user names stripped
- self.assertEqual(['ssh-rsa AA2..+aRD0fyVw== root@server'],
- self.ds.get_public_ssh_keys())
-
# test partial metadata (missing user-data in particular)
- @httpretty.activate
def test_metadata_partial(self):
- httpretty.register_uri(
- httpretty.GET, MD_URL_RE,
- body=_new_request_callback(GCE_META_PARTIAL))
+ _set_mock_metadata(GCE_META_PARTIAL)
self.ds.get_data()
self.assertEqual(GCE_META_PARTIAL.get('instance/id'),
@@ -130,13 +115,52 @@ class TestDataSourceGCE(test_helpers.HttprettyTestCase):
shostname = GCE_META_PARTIAL.get('instance/hostname').split('.')[0]
self.assertEqual(shostname, self.ds.get_hostname())
- @httpretty.activate
def test_metadata_encoding(self):
- httpretty.register_uri(
- httpretty.GET, MD_URL_RE,
- body=_new_request_callback(GCE_META_ENCODING))
+ _set_mock_metadata(GCE_META_ENCODING)
self.ds.get_data()
decoded = b64decode(
GCE_META_ENCODING.get('instance/attributes/user-data'))
self.assertEqual(decoded, self.ds.get_userdata_raw())
+
+ def test_missing_required_keys_return_false(self):
+ for required_key in ['instance/id', 'instance/zone',
+ 'instance/hostname']:
+ meta = GCE_META_PARTIAL.copy()
+ del meta[required_key]
+ _set_mock_metadata(meta)
+ self.assertEqual(False, self.ds.get_data())
+ httpretty.reset()
+
+ def test_project_level_ssh_keys_are_used(self):
+ _set_mock_metadata()
+ self.ds.get_data()
+
+ # we expect a list of public ssh keys with user names stripped
+ self.assertEqual(['ssh-rsa AA2..+aRD0fyVw== root@server'],
+ self.ds.get_public_ssh_keys())
+
+ def test_instance_level_ssh_keys_are_used(self):
+ key_content = 'ssh-rsa JustAUser root@server'
+ meta = GCE_META.copy()
+ meta['instance/attributes/sshKeys'] = 'user:{0}'.format(key_content)
+
+ _set_mock_metadata(meta)
+ self.ds.get_data()
+
+ self.assertIn(key_content, self.ds.get_public_ssh_keys())
+
+ def test_instance_level_keys_replace_project_level_keys(self):
+ key_content = 'ssh-rsa JustAUser root@server'
+ meta = GCE_META.copy()
+ meta['instance/attributes/sshKeys'] = 'user:{0}'.format(key_content)
+
+ _set_mock_metadata(meta)
+ self.ds.get_data()
+
+ self.assertEqual([key_content], self.ds.get_public_ssh_keys())
+
+ def test_only_last_part_of_zone_used_for_availability_zone(self):
+ _set_mock_metadata()
+ self.ds.get_data()
+ self.assertEqual('bar', self.ds.availability_zone)
diff --git a/tests/unittests/test_datasource/test_maas.py b/tests/unittests/test_datasource/test_maas.py
index c157beb8..77d15cac 100644
--- a/tests/unittests/test_datasource/test_maas.py
+++ b/tests/unittests/test_datasource/test_maas.py
@@ -1,27 +1,33 @@
from copy import copy
import os
+import shutil
+import tempfile
from cloudinit.sources import DataSourceMAAS
from cloudinit import url_helper
-from ..helpers import populate_dir
+from ..helpers import TestCase, populate_dir
-import mocker
+try:
+ from unittest import mock
+except ImportError:
+ import mock
-class TestMAASDataSource(mocker.MockerTestCase):
+class TestMAASDataSource(TestCase):
def setUp(self):
super(TestMAASDataSource, self).setUp()
# Make a temp directoy for tests to use.
- self.tmp = self.makeDir()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def test_seed_dir_valid(self):
"""Verify a valid seeddir is read as such."""
data = {'instance-id': 'i-valid01',
- 'local-hostname': 'valid01-hostname',
- 'user-data': 'valid01-userdata',
- 'public-keys': 'ssh-rsa AAAAB3Nz...aC1yc2E= keyname'}
+ 'local-hostname': 'valid01-hostname',
+ 'user-data': b'valid01-userdata',
+ 'public-keys': 'ssh-rsa AAAAB3Nz...aC1yc2E= keyname'}
my_d = os.path.join(self.tmp, "valid")
populate_dir(my_d, data)
@@ -39,8 +45,8 @@ class TestMAASDataSource(mocker.MockerTestCase):
"""Verify extra files do not affect seed_dir validity."""
data = {'instance-id': 'i-valid-extra',
- 'local-hostname': 'valid-extra-hostname',
- 'user-data': 'valid-extra-userdata', 'foo': 'bar'}
+ 'local-hostname': 'valid-extra-hostname',
+ 'user-data': b'valid-extra-userdata', 'foo': 'bar'}
my_d = os.path.join(self.tmp, "valid_extra")
populate_dir(my_d, data)
@@ -58,7 +64,7 @@ class TestMAASDataSource(mocker.MockerTestCase):
"""Verify that invalid seed_dir raises MAASSeedDirMalformed."""
valid = {'instance-id': 'i-instanceid',
- 'local-hostname': 'test-hostname', 'user-data': ''}
+ 'local-hostname': 'test-hostname', 'user-data': ''}
my_based = os.path.join(self.tmp, "valid_extra")
@@ -88,21 +94,23 @@ class TestMAASDataSource(mocker.MockerTestCase):
def test_seed_dir_missing(self):
"""Verify that missing seed_dir raises MAASSeedDirNone."""
self.assertRaises(DataSourceMAAS.MAASSeedDirNone,
- DataSourceMAAS.read_maas_seed_dir,
- os.path.join(self.tmp, "nonexistantdirectory"))
+ DataSourceMAAS.read_maas_seed_dir,
+ os.path.join(self.tmp, "nonexistantdirectory"))
def test_seed_url_valid(self):
"""Verify that valid seed_url is read as such."""
- valid = {'meta-data/instance-id': 'i-instanceid',
+ valid = {
+ 'meta-data/instance-id': 'i-instanceid',
'meta-data/local-hostname': 'test-hostname',
'meta-data/public-keys': 'test-hostname',
- 'user-data': 'foodata'}
+ 'user-data': b'foodata',
+ }
valid_order = [
'meta-data/local-hostname',
'meta-data/instance-id',
'meta-data/public-keys',
'user-data',
- ]
+ ]
my_seed = "http://example.com/xmeta"
my_ver = "1999-99-99"
my_headers = {'header1': 'value1', 'header2': 'value2'}
@@ -110,28 +118,38 @@ class TestMAASDataSource(mocker.MockerTestCase):
def my_headers_cb(url):
return my_headers
- mock_request = self.mocker.replace(url_helper.readurl,
- passthrough=False)
-
- for key in valid_order:
- url = "%s/%s/%s" % (my_seed, my_ver, key)
- mock_request(url, headers=None, timeout=mocker.ANY,
- data=mocker.ANY, sec_between=mocker.ANY,
- ssl_details=mocker.ANY, retries=mocker.ANY,
- headers_cb=my_headers_cb,
- exception_cb=mocker.ANY)
- resp = valid.get(key)
- self.mocker.result(url_helper.StringResponse(resp))
- self.mocker.replay()
-
- (userdata, metadata) = DataSourceMAAS.read_maas_seed_url(my_seed,
- header_cb=my_headers_cb, version=my_ver)
-
- self.assertEqual("foodata", userdata)
- self.assertEqual(metadata['instance-id'],
- valid['meta-data/instance-id'])
- self.assertEqual(metadata['local-hostname'],
- valid['meta-data/local-hostname'])
+ # Each time url_helper.readurl() is called, something different is
+ # returned based on the canned data above. We need to build up a list
+ # of side effect return values, which the mock will return. At the
+ # same time, we'll build up a list of expected call arguments for
+ # asserting after the code under test is run.
+ calls = []
+
+ def side_effect():
+ for key in valid_order:
+ resp = valid.get(key)
+ url = "%s/%s/%s" % (my_seed, my_ver, key)
+ calls.append(
+ mock.call(url, headers=None, timeout=mock.ANY,
+ data=mock.ANY, sec_between=mock.ANY,
+ ssl_details=mock.ANY, retries=mock.ANY,
+ headers_cb=my_headers_cb,
+ exception_cb=mock.ANY))
+ yield url_helper.StringResponse(resp)
+
+ # Now do the actual call of the code under test.
+ with mock.patch.object(url_helper, 'readurl',
+ side_effect=side_effect()) as mockobj:
+ userdata, metadata = DataSourceMAAS.read_maas_seed_url(
+ my_seed, version=my_ver)
+
+ self.assertEqual(b"foodata", userdata)
+ self.assertEqual(metadata['instance-id'],
+ valid['meta-data/instance-id'])
+ self.assertEqual(metadata['local-hostname'],
+ valid['meta-data/local-hostname'])
+
+ mockobj.has_calls(calls)
def test_seed_url_invalid(self):
"""Verify that invalid seed_url raises MAASSeedDirMalformed."""
diff --git a/tests/unittests/test_datasource/test_nocloud.py b/tests/unittests/test_datasource/test_nocloud.py
index e9235951..2d5fc37c 100644
--- a/tests/unittests/test_datasource/test_nocloud.py
+++ b/tests/unittests/test_datasource/test_nocloud.py
@@ -1,39 +1,43 @@
from cloudinit import helpers
from cloudinit.sources import DataSourceNoCloud
from cloudinit import util
-from ..helpers import populate_dir
+from ..helpers import TestCase, populate_dir
-from mocker import MockerTestCase
import os
import yaml
+import shutil
+import tempfile
+import unittest
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
-class TestNoCloudDataSource(MockerTestCase):
+
+class TestNoCloudDataSource(TestCase):
def setUp(self):
- self.tmp = self.makeDir()
+ super(TestNoCloudDataSource, self).setUp()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
self.paths = helpers.Paths({'cloud_dir': self.tmp})
self.cmdline = "root=TESTCMDLINE"
- self.unapply = []
- self.apply_patches([(util, 'get_cmdline', self._getcmdline)])
- super(TestNoCloudDataSource, self).setUp()
-
- def tearDown(self):
- apply_patches([i for i in reversed(self.unapply)])
- super(TestNoCloudDataSource, self).tearDown()
+ self.mocks = ExitStack()
+ self.addCleanup(self.mocks.close)
- def apply_patches(self, patches):
- ret = apply_patches(patches)
- self.unapply += ret
-
- def _getcmdline(self):
- return self.cmdline
+ self.mocks.enter_context(
+ mock.patch.object(util, 'get_cmdline', return_value=self.cmdline))
def test_nocloud_seed_dir(self):
md = {'instance-id': 'IID', 'dsmode': 'local'}
- ud = "USER_DATA_HERE"
+ ud = b"USER_DATA_HERE"
populate_dir(os.path.join(self.paths.seed_dir, "nocloud"),
{'user-data': ud, 'meta-data': yaml.safe_dump(md)})
@@ -59,7 +63,9 @@ class TestNoCloudDataSource(MockerTestCase):
def my_find_devs_with(*args, **kwargs):
raise PsuedoException
- self.apply_patches([(util, 'find_devs_with', my_find_devs_with)])
+ self.mocks.enter_context(
+ mock.patch.object(util, 'find_devs_with',
+ side_effect=PsuedoException))
# by default, NoCloud should search for filesystems by label
sys_cfg = {'datasource': {'NoCloud': {}}}
@@ -85,21 +91,21 @@ class TestNoCloudDataSource(MockerTestCase):
data = {
'fs_label': None,
- 'meta-data': {'instance-id': 'IID'},
- 'user-data': "USER_DATA_RAW",
+ 'meta-data': yaml.safe_dump({'instance-id': 'IID'}),
+ 'user-data': b"USER_DATA_RAW",
}
sys_cfg = {'datasource': {'NoCloud': data}}
dsrc = ds(sys_cfg=sys_cfg, distro=None, paths=self.paths)
ret = dsrc.get_data()
- self.assertEqual(dsrc.userdata_raw, "USER_DATA_RAW")
+ self.assertEqual(dsrc.userdata_raw, b"USER_DATA_RAW")
self.assertEqual(dsrc.metadata.get('instance-id'), 'IID')
self.assertTrue(ret)
def test_nocloud_seed_with_vendordata(self):
md = {'instance-id': 'IID', 'dsmode': 'local'}
- ud = "USER_DATA_HERE"
- vd = "THIS IS MY VENDOR_DATA"
+ ud = b"USER_DATA_HERE"
+ vd = b"THIS IS MY VENDOR_DATA"
populate_dir(os.path.join(self.paths.seed_dir, "nocloud"),
{'user-data': ud, 'meta-data': yaml.safe_dump(md),
@@ -115,12 +121,12 @@ class TestNoCloudDataSource(MockerTestCase):
ret = dsrc.get_data()
self.assertEqual(dsrc.userdata_raw, ud)
self.assertEqual(dsrc.metadata, md)
- self.assertEqual(dsrc.vendordata, vd)
+ self.assertEqual(dsrc.vendordata_raw, vd)
self.assertTrue(ret)
def test_nocloud_no_vendordata(self):
populate_dir(os.path.join(self.paths.seed_dir, "nocloud"),
- {'user-data': "ud", 'meta-data': "instance-id: IID\n"})
+ {'user-data': b"ud", 'meta-data': "instance-id: IID\n"})
sys_cfg = {'datasource': {'NoCloud': {'fs_label': None}}}
@@ -128,12 +134,12 @@ class TestNoCloudDataSource(MockerTestCase):
dsrc = ds(sys_cfg=sys_cfg, distro=None, paths=self.paths)
ret = dsrc.get_data()
- self.assertEqual(dsrc.userdata_raw, "ud")
+ self.assertEqual(dsrc.userdata_raw, b"ud")
self.assertFalse(dsrc.vendordata)
self.assertTrue(ret)
-class TestParseCommandLineData(MockerTestCase):
+class TestParseCommandLineData(unittest.TestCase):
def test_parse_cmdline_data_valid(self):
ds_id = "ds=nocloud"
@@ -178,15 +184,4 @@ class TestParseCommandLineData(MockerTestCase):
self.assertFalse(ret)
-def apply_patches(patches):
- ret = []
- for (ref, name, replace) in patches:
- if replace is None:
- continue
- orig = getattr(ref, name)
- setattr(ref, name, replace)
- ret.append((ref, name, orig))
- return ret
-
-
# vi: ts=4 expandtab
diff --git a/tests/unittests/test_datasource/test_opennebula.py b/tests/unittests/test_datasource/test_opennebula.py
index b4fd1f4d..d796f030 100644
--- a/tests/unittests/test_datasource/test_opennebula.py
+++ b/tests/unittests/test_datasource/test_opennebula.py
@@ -1,12 +1,14 @@
from cloudinit import helpers
from cloudinit.sources import DataSourceOpenNebula as ds
from cloudinit import util
-from mocker import MockerTestCase
-from ..helpers import populate_dir
+from ..helpers import TestCase, populate_dir
-from base64 import b64encode
import os
import pwd
+import shutil
+import tempfile
+import unittest
+
TEST_VARS = {
'VAR1': 'single',
@@ -18,7 +20,7 @@ TEST_VARS = {
'VAR7': 'single\\t',
'VAR8': 'double\\tword',
'VAR9': 'multi\\t\nline\n',
- 'VAR10': '\\', # expect \
+ 'VAR10': '\\', # expect '\'
'VAR11': '\'', # expect '
'VAR12': '$', # expect $
}
@@ -37,12 +39,13 @@ CMD_IP_OUT = '''\
'''
-class TestOpenNebulaDataSource(MockerTestCase):
+class TestOpenNebulaDataSource(TestCase):
parsed_user = None
def setUp(self):
super(TestOpenNebulaDataSource, self).setUp()
- self.tmp = self.makeDir()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
self.paths = helpers.Paths({'cloud_dir': self.tmp})
# defaults for few tests
@@ -176,7 +179,7 @@ class TestOpenNebulaDataSource(MockerTestCase):
self.assertEqual(USER_DATA, results['userdata'])
def test_user_data_encoding_required_for_decode(self):
- b64userdata = b64encode(USER_DATA)
+ b64userdata = util.b64e(USER_DATA)
for k in ('USER_DATA', 'USERDATA'):
my_d = os.path.join(self.tmp, k)
populate_context_dir(my_d, {k: b64userdata})
@@ -188,7 +191,7 @@ class TestOpenNebulaDataSource(MockerTestCase):
def test_user_data_base64_encoding(self):
for k in ('USER_DATA', 'USERDATA'):
my_d = os.path.join(self.tmp, k)
- populate_context_dir(my_d, {k: b64encode(USER_DATA),
+ populate_context_dir(my_d, {k: util.b64e(USER_DATA),
'USERDATA_ENCODING': 'base64'})
results = ds.read_context_disk_dir(my_d)
@@ -228,7 +231,7 @@ class TestOpenNebulaDataSource(MockerTestCase):
util.find_devs_with = orig_find_devs_with
-class TestOpenNebulaNetwork(MockerTestCase):
+class TestOpenNebulaNetwork(unittest.TestCase):
def setUp(self):
super(TestOpenNebulaNetwork, self).setUp()
@@ -280,7 +283,7 @@ iface eth0 inet static
''')
-class TestParseShellConfig(MockerTestCase):
+class TestParseShellConfig(unittest.TestCase):
def test_no_seconds(self):
cfg = '\n'.join(["foo=bar", "SECONDS=2", "xx=foo"])
# we could test 'sleep 2', but that would make the test run slower.
@@ -290,7 +293,7 @@ class TestParseShellConfig(MockerTestCase):
def populate_context_dir(path, variables):
data = "# Context variables generated by OpenNebula\n"
- for (k, v) in variables.iteritems():
+ for k, v in variables.items():
data += ("%s='%s'\n" % (k.upper(), v.replace(r"'", r"'\''")))
populate_dir(path, {'context.sh': data})
diff --git a/tests/unittests/test_datasource/test_openstack.py b/tests/unittests/test_datasource/test_openstack.py
index 49894e51..0aa1ba84 100644
--- a/tests/unittests/test_datasource/test_openstack.py
+++ b/tests/unittests/test_datasource/test_openstack.py
@@ -20,19 +20,18 @@ import copy
import json
import re
-from StringIO import StringIO
-
-from urlparse import urlparse
-
from .. import helpers as test_helpers
+from six import StringIO
+from six.moves.urllib.parse import urlparse
+
from cloudinit import helpers
from cloudinit import settings
from cloudinit.sources import DataSourceOpenStack as ds
from cloudinit.sources.helpers import openstack
from cloudinit import util
-import httpretty as hp
+hp = test_helpers.import_httpretty()
BASE_URL = "http://169.254.169.254"
PUBKEY = u'ssh-rsa AAAAB3NzaC1....sIkJhq8wdX+4I3A4cYbYP ubuntu@server-460\n'
@@ -50,7 +49,7 @@ EC2_META = {
'public-ipv4': '0.0.0.1',
'reservation-id': 'r-iru5qm4m',
}
-USER_DATA = '#!/bin/sh\necho This is user data\n'
+USER_DATA = b'#!/bin/sh\necho This is user data\n'
VENDOR_DATA = {
'magic': '',
}
@@ -64,8 +63,8 @@ OSTACK_META = {
'public_keys': {'mykey': PUBKEY},
'uuid': 'b0fa911b-69d4-4476-bbe2-1c92bff6535c',
}
-CONTENT_0 = 'This is contents of /etc/foo.cfg\n'
-CONTENT_1 = '# this is /etc/bar/bar.cfg\n'
+CONTENT_0 = b'This is contents of /etc/foo.cfg\n'
+CONTENT_1 = b'# this is /etc/bar/bar.cfg\n'
OS_FILES = {
'openstack/latest/meta_data.json': json.dumps(OSTACK_META),
'openstack/latest/user_data': USER_DATA,
diff --git a/tests/unittests/test_datasource/test_smartos.py b/tests/unittests/test_datasource/test_smartos.py
index 65675106..5c49966a 100644
--- a/tests/unittests/test_datasource/test_smartos.py
+++ b/tests/unittests/test_datasource/test_smartos.py
@@ -22,15 +22,30 @@
# return responses.
#
-import base64
-from cloudinit import helpers as c_helpers
-from cloudinit.sources import DataSourceSmartOS
-from .. import helpers
+from __future__ import print_function
+
import os
import os.path
import re
+import shutil
import stat
+import tempfile
import uuid
+from binascii import crc32
+
+import serial
+import six
+
+from cloudinit import helpers as c_helpers
+from cloudinit.sources import DataSourceSmartOS
+from cloudinit.util import b64e
+
+from .. import helpers
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
MOCK_RETURNS = {
'hostname': 'test-host',
@@ -41,77 +56,34 @@ MOCK_RETURNS = {
'cloud-init:user-data': '\n'.join(['#!/bin/sh', '/bin/true', '']),
'sdc:datacenter_name': 'somewhere2',
'sdc:operator-script': '\n'.join(['bin/true', '']),
+ 'sdc:uuid': str(uuid.uuid4()),
'sdc:vendor-data': '\n'.join(['VENDOR_DATA', '']),
'user-data': '\n'.join(['something', '']),
'user-script': '\n'.join(['/bin/true', '']),
}
-DMI_DATA_RETURN = (str(uuid.uuid4()), 'smartdc')
-
-
-class MockSerial(object):
- """Fake a serial terminal for testing the code that
- interfaces with the serial"""
-
- port = None
-
- def __init__(self, mockdata):
- self.last = None
- self.last = None
- self.new = True
- self.count = 0
- self.mocked_out = []
- self.mockdata = mockdata
-
- def open(self):
- return True
-
- def close(self):
- return True
-
- def isOpen(self):
- return True
+DMI_DATA_RETURN = 'smartdc'
- def write(self, line):
- line = line.replace('GET ', '')
- self.last = line.rstrip()
- def readline(self):
- if self.new:
- self.new = False
- if self.last in self.mockdata:
- return 'SUCCESS\n'
- else:
- return 'NOTFOUND %s\n' % self.last
-
- if self.last in self.mockdata:
- if not self.mocked_out:
- self.mocked_out = [x for x in self._format_out()]
-
- if len(self.mocked_out) > self.count:
- self.count += 1
- return self.mocked_out[self.count - 1]
+def get_mock_client(mockdata):
+ class MockMetadataClient(object):
- def _format_out(self):
- if self.last in self.mockdata:
- _mret = self.mockdata[self.last]
- try:
- for l in _mret.splitlines():
- yield "%s\n" % l.rstrip()
- except:
- yield "%s\n" % _mret.rstrip()
+ def __init__(self, serial):
+ pass
- yield '.'
- yield '\n'
+ def get_metadata(self, metadata_key):
+ return mockdata.get(metadata_key)
+ return MockMetadataClient
class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
def setUp(self):
- helpers.FilesystemMockingTestCase.setUp(self)
+ super(TestSmartOSDataSource, self).setUp()
- # makeDir comes from MockerTestCase
- self.tmp = self.makeDir()
- self.legacy_user_d = self.makeDir()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
+ self.legacy_user_d = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.legacy_user_d)
# If you should want to watch the logs...
self._log = None
@@ -140,7 +112,8 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
ret = apply_patches(patches)
self.unapply += ret
- def _get_ds(self, sys_cfg=None, ds_cfg=None, mockdata=None, dmi_data=None):
+ def _get_ds(self, sys_cfg=None, ds_cfg=None, mockdata=None, dmi_data=None,
+ is_lxbrand=False):
mod = DataSourceSmartOS
if mockdata is None:
@@ -149,16 +122,17 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
if dmi_data is None:
dmi_data = DMI_DATA_RETURN
- def _get_serial(*_):
- return MockSerial(mockdata)
-
def _dmi_data():
return dmi_data
def _os_uname():
- # LP: #1243287. tests assume this runs, but running test on
- # arm would cause them all to fail.
- return ('LINUX', 'NODENAME', 'RELEASE', 'VERSION', 'x86_64')
+ if not is_lxbrand:
+ # LP: #1243287. tests assume this runs, but running test on
+ # arm would cause them all to fail.
+ return ('LINUX', 'NODENAME', 'RELEASE', 'VERSION', 'x86_64')
+ else:
+ return ('LINUX', 'NODENAME', 'RELEASE', 'BRANDZ VIRTUAL LINUX',
+ 'X86_64')
if sys_cfg is None:
sys_cfg = {}
@@ -168,12 +142,14 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
sys_cfg['datasource']['SmartOS'] = ds_cfg
self.apply_patches([(mod, 'LEGACY_USER_D', self.legacy_user_d)])
- self.apply_patches([(mod, 'get_serial', _get_serial)])
+ self.apply_patches([
+ (mod, 'JoyentMetadataClient', get_mock_client(mockdata))])
self.apply_patches([(mod, 'dmi_data', _dmi_data)])
self.apply_patches([(os, 'uname', _os_uname)])
self.apply_patches([(mod, 'device_exists', lambda d: True)])
dsrc = mod.DataSourceSmartOS(sys_cfg, distro=None,
paths=self.paths)
+ self.apply_patches([(dsrc, '_get_seed_file_object', mock.MagicMock())])
return dsrc
def test_seed(self):
@@ -181,14 +157,29 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
dsrc = self._get_ds()
ret = dsrc.get_data()
self.assertTrue(ret)
+ self.assertEquals('kvm', dsrc.smartos_type)
self.assertEquals('/dev/ttyS1', dsrc.seed)
+ def test_seed_lxbrand(self):
+ # default seed should be /dev/ttyS1
+ dsrc = self._get_ds(is_lxbrand=True)
+ ret = dsrc.get_data()
+ self.assertTrue(ret)
+ self.assertEquals('lx-brand', dsrc.smartos_type)
+ self.assertEquals('/native/.zonecontrol/metadata.sock', dsrc.seed)
+
def test_issmartdc(self):
dsrc = self._get_ds()
ret = dsrc.get_data()
self.assertTrue(ret)
self.assertTrue(dsrc.is_smartdc)
+ def test_issmartdc_lxbrand(self):
+ dsrc = self._get_ds(is_lxbrand=True)
+ ret = dsrc.get_data()
+ self.assertTrue(ret)
+ self.assertTrue(dsrc.is_smartdc)
+
def test_no_base64(self):
ds_cfg = {'no_base64_decode': ['test_var1'], 'all_base': True}
dsrc = self._get_ds(ds_cfg=ds_cfg)
@@ -199,7 +190,8 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
dsrc = self._get_ds(mockdata=MOCK_RETURNS)
ret = dsrc.get_data()
self.assertTrue(ret)
- self.assertEquals(DMI_DATA_RETURN[0], dsrc.metadata['instance-id'])
+ self.assertEquals(MOCK_RETURNS['sdc:uuid'],
+ dsrc.metadata['instance-id'])
def test_root_keys(self):
dsrc = self._get_ds(mockdata=MOCK_RETURNS)
@@ -227,7 +219,7 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
my_returns = MOCK_RETURNS.copy()
my_returns['base64_all'] = "true"
for k in ('hostname', 'cloud-init:user-data'):
- my_returns[k] = base64.b64encode(my_returns[k])
+ my_returns[k] = b64e(my_returns[k])
dsrc = self._get_ds(mockdata=my_returns)
ret = dsrc.get_data()
@@ -248,7 +240,7 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
my_returns['b64-cloud-init:user-data'] = "true"
my_returns['b64-hostname'] = "true"
for k in ('hostname', 'cloud-init:user-data'):
- my_returns[k] = base64.b64encode(my_returns[k])
+ my_returns[k] = b64e(my_returns[k])
dsrc = self._get_ds(mockdata=my_returns)
ret = dsrc.get_data()
@@ -264,7 +256,7 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
my_returns = MOCK_RETURNS.copy()
my_returns['base64_keys'] = 'hostname,ignored'
for k in ('hostname',):
- my_returns[k] = base64.b64encode(my_returns[k])
+ my_returns[k] = b64e(my_returns[k])
dsrc = self._get_ds(mockdata=my_returns)
ret = dsrc.get_data()
@@ -365,7 +357,7 @@ class TestSmartOSDataSource(helpers.FilesystemMockingTestCase):
permissions = oct(os.stat(name_f)[stat.ST_MODE])[-3:]
if re.match(r'.*\/mdata-user-data$', name_f):
found_new = True
- print name_f
+ print(name_f)
self.assertEquals(permissions, '400')
self.assertFalse(found_new)
@@ -447,3 +439,147 @@ def apply_patches(patches):
setattr(ref, name, replace)
ret.append((ref, name, orig))
return ret
+
+
+class TestJoyentMetadataClient(helpers.FilesystemMockingTestCase):
+
+ def setUp(self):
+ super(TestJoyentMetadataClient, self).setUp()
+ self.serial = mock.MagicMock(spec=serial.Serial)
+ self.request_id = 0xabcdef12
+ self.metadata_value = 'value'
+ self.response_parts = {
+ 'command': 'SUCCESS',
+ 'crc': 'b5a9ff00',
+ 'length': 17 + len(b64e(self.metadata_value)),
+ 'payload': b64e(self.metadata_value),
+ 'request_id': '{0:08x}'.format(self.request_id),
+ }
+
+ def make_response():
+ payloadstr = ''
+ if 'payload' in self.response_parts:
+ payloadstr = ' {0}'.format(self.response_parts['payload'])
+ return ('V2 {length} {crc} {request_id} '
+ '{command}{payloadstr}\n'.format(
+ payloadstr=payloadstr,
+ **self.response_parts).encode('ascii'))
+
+ self.metasource_data = None
+
+ def read_response(length):
+ if not self.metasource_data:
+ self.metasource_data = make_response()
+ self.metasource_data_len = len(self.metasource_data)
+ resp = self.metasource_data[:length]
+ self.metasource_data = self.metasource_data[length:]
+ return resp
+
+ self.serial.read.side_effect = read_response
+ self.patched_funcs.enter_context(
+ mock.patch('cloudinit.sources.DataSourceSmartOS.random.randint',
+ mock.Mock(return_value=self.request_id)))
+
+ def _get_client(self):
+ return DataSourceSmartOS.JoyentMetadataClient(self.serial)
+
+ def assertEndsWith(self, haystack, prefix):
+ self.assertTrue(haystack.endswith(prefix),
+ "{0} does not end with '{1}'".format(
+ repr(haystack), prefix))
+
+ def assertStartsWith(self, haystack, prefix):
+ self.assertTrue(haystack.startswith(prefix),
+ "{0} does not start with '{1}'".format(
+ repr(haystack), prefix))
+
+ def test_get_metadata_writes_a_single_line(self):
+ client = self._get_client()
+ client.get_metadata('some_key')
+ self.assertEqual(1, self.serial.write.call_count)
+ written_line = self.serial.write.call_args[0][0]
+ print(type(written_line))
+ self.assertEndsWith(written_line.decode('ascii'),
+ b'\n'.decode('ascii'))
+ self.assertEqual(1, written_line.count(b'\n'))
+
+ def _get_written_line(self, key='some_key'):
+ client = self._get_client()
+ client.get_metadata(key)
+ return self.serial.write.call_args[0][0]
+
+ def test_get_metadata_writes_bytes(self):
+ self.assertIsInstance(self._get_written_line(), six.binary_type)
+
+ def test_get_metadata_line_starts_with_v2(self):
+ foo = self._get_written_line()
+ self.assertStartsWith(foo.decode('ascii'), b'V2'.decode('ascii'))
+
+ def test_get_metadata_uses_get_command(self):
+ parts = self._get_written_line().decode('ascii').strip().split(' ')
+ self.assertEqual('GET', parts[4])
+
+ def test_get_metadata_base64_encodes_argument(self):
+ key = 'my_key'
+ parts = self._get_written_line(key).decode('ascii').strip().split(' ')
+ self.assertEqual(b64e(key), parts[5])
+
+ def test_get_metadata_calculates_length_correctly(self):
+ parts = self._get_written_line().decode('ascii').strip().split(' ')
+ expected_length = len(' '.join(parts[3:]))
+ self.assertEqual(expected_length, int(parts[1]))
+
+ def test_get_metadata_uses_appropriate_request_id(self):
+ parts = self._get_written_line().decode('ascii').strip().split(' ')
+ request_id = parts[3]
+ self.assertEqual(8, len(request_id))
+ self.assertEqual(request_id, request_id.lower())
+
+ def test_get_metadata_uses_random_number_for_request_id(self):
+ line = self._get_written_line()
+ request_id = line.decode('ascii').strip().split(' ')[3]
+ self.assertEqual('{0:08x}'.format(self.request_id), request_id)
+
+ def test_get_metadata_checksums_correctly(self):
+ parts = self._get_written_line().decode('ascii').strip().split(' ')
+ expected_checksum = '{0:08x}'.format(
+ crc32(' '.join(parts[3:]).encode('utf-8')) & 0xffffffff)
+ checksum = parts[2]
+ self.assertEqual(expected_checksum, checksum)
+
+ def test_get_metadata_reads_a_line(self):
+ client = self._get_client()
+ client.get_metadata('some_key')
+ self.assertEqual(self.metasource_data_len, self.serial.read.call_count)
+
+ def test_get_metadata_returns_valid_value(self):
+ client = self._get_client()
+ value = client.get_metadata('some_key')
+ self.assertEqual(self.metadata_value, value)
+
+ def test_get_metadata_throws_exception_for_incorrect_length(self):
+ self.response_parts['length'] = 0
+ client = self._get_client()
+ self.assertRaises(DataSourceSmartOS.JoyentMetadataFetchException,
+ client.get_metadata, 'some_key')
+
+ def test_get_metadata_throws_exception_for_incorrect_crc(self):
+ self.response_parts['crc'] = 'deadbeef'
+ client = self._get_client()
+ self.assertRaises(DataSourceSmartOS.JoyentMetadataFetchException,
+ client.get_metadata, 'some_key')
+
+ def test_get_metadata_throws_exception_for_request_id_mismatch(self):
+ self.response_parts['request_id'] = 'deadbeef'
+ client = self._get_client()
+ client._checksum = lambda _: self.response_parts['crc']
+ self.assertRaises(DataSourceSmartOS.JoyentMetadataFetchException,
+ client.get_metadata, 'some_key')
+
+ def test_get_metadata_returns_None_if_value_not_found(self):
+ self.response_parts['payload'] = ''
+ self.response_parts['command'] = 'NOTFOUND'
+ self.response_parts['length'] = 17
+ client = self._get_client()
+ client._checksum = lambda _: self.response_parts['crc']
+ self.assertIsNone(client.get_metadata('some_key'))
diff --git a/tests/unittests/test_distros/test_generic.py b/tests/unittests/test_distros/test_generic.py
index db6aa0e8..6ed1704c 100644
--- a/tests/unittests/test_distros/test_generic.py
+++ b/tests/unittests/test_distros/test_generic.py
@@ -4,6 +4,13 @@ from cloudinit import util
from .. import helpers
import os
+import shutil
+import tempfile
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
unknown_arch_info = {
'arches': ['default'],
@@ -53,7 +60,8 @@ class TestGenericDistro(helpers.FilesystemMockingTestCase):
def setUp(self):
super(TestGenericDistro, self).setUp()
# Make a temp directoy for tests to use.
- self.tmp = self.makeDir()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def _write_load_sudoers(self, _user, rules):
cls = distros.fetch("ubuntu")
@@ -64,7 +72,6 @@ class TestGenericDistro(helpers.FilesystemMockingTestCase):
self.patchUtils(self.tmp)
d.write_sudo_rules("harlowja", rules)
contents = util.load_file(d.ci_sudoers_fn)
- self.restore()
return contents
def _count_in(self, lines_look_for, text_content):
@@ -142,33 +149,35 @@ class TestGenericDistro(helpers.FilesystemMockingTestCase):
def test_get_package_mirror_info_az_ec2(self):
arch_mirrors = gapmi(package_mirrors, arch="amd64")
+ data_source_mock = mock.Mock(availability_zone="us-east-1a")
- results = gpmi(arch_mirrors, availability_zone="us-east-1a",
+ results = gpmi(arch_mirrors, data_source=data_source_mock,
mirror_filter=self.return_first)
self.assertEqual(results,
{'primary': 'http://us-east-1.ec2/',
'security': 'http://security-mirror1-intel'})
- results = gpmi(arch_mirrors, availability_zone="us-east-1a",
+ results = gpmi(arch_mirrors, data_source=data_source_mock,
mirror_filter=self.return_second)
self.assertEqual(results,
{'primary': 'http://us-east-1a.clouds/',
'security': 'http://security-mirror2-intel'})
- results = gpmi(arch_mirrors, availability_zone="us-east-1a",
+ results = gpmi(arch_mirrors, data_source=data_source_mock,
mirror_filter=self.return_none)
self.assertEqual(results, package_mirrors[0]['failsafe'])
def test_get_package_mirror_info_az_non_ec2(self):
arch_mirrors = gapmi(package_mirrors, arch="amd64")
+ data_source_mock = mock.Mock(availability_zone="nova.cloudvendor")
- results = gpmi(arch_mirrors, availability_zone="nova.cloudvendor",
+ results = gpmi(arch_mirrors, data_source=data_source_mock,
mirror_filter=self.return_first)
self.assertEqual(results,
{'primary': 'http://nova.cloudvendor.clouds/',
'security': 'http://security-mirror1-intel'})
- results = gpmi(arch_mirrors, availability_zone="nova.cloudvendor",
+ results = gpmi(arch_mirrors, data_source=data_source_mock,
mirror_filter=self.return_last)
self.assertEqual(results,
{'primary': 'http://nova.cloudvendor.clouds/',
@@ -176,22 +185,46 @@ class TestGenericDistro(helpers.FilesystemMockingTestCase):
def test_get_package_mirror_info_none(self):
arch_mirrors = gapmi(package_mirrors, arch="amd64")
+ data_source_mock = mock.Mock(availability_zone=None)
# because both search entries here replacement based on
# availability-zone, the filter will be called with an empty list and
# failsafe should be taken.
- results = gpmi(arch_mirrors, availability_zone=None,
+ results = gpmi(arch_mirrors, data_source=data_source_mock,
mirror_filter=self.return_first)
self.assertEqual(results,
{'primary': 'http://fs-primary-intel',
'security': 'http://security-mirror1-intel'})
- results = gpmi(arch_mirrors, availability_zone=None,
+ results = gpmi(arch_mirrors, data_source=data_source_mock,
mirror_filter=self.return_last)
self.assertEqual(results,
{'primary': 'http://fs-primary-intel',
'security': 'http://security-mirror2-intel'})
+ def test_systemd_in_use(self):
+ cls = distros.fetch("ubuntu")
+ d = cls("ubuntu", {}, None)
+ self.patchOS(self.tmp)
+ self.patchUtils(self.tmp)
+ os.makedirs('/run/systemd/system')
+ self.assertTrue(d.uses_systemd())
+
+ def test_systemd_not_in_use(self):
+ cls = distros.fetch("ubuntu")
+ d = cls("ubuntu", {}, None)
+ self.patchOS(self.tmp)
+ self.patchUtils(self.tmp)
+ self.assertFalse(d.uses_systemd())
+
+ def test_systemd_symlink(self):
+ cls = distros.fetch("ubuntu")
+ d = cls("ubuntu", {}, None)
+ self.patchOS(self.tmp)
+ self.patchUtils(self.tmp)
+ os.makedirs('/run/systemd')
+ os.symlink('/', '/run/systemd/system')
+ self.assertFalse(d.uses_systemd())
# def _get_package_mirror_info(mirror_info, availability_zone=None,
# mirror_filter=util.search_for_mirror):
diff --git a/tests/unittests/test_distros/test_hostname.py b/tests/unittests/test_distros/test_hostname.py
index 8e644f4d..143e29a9 100644
--- a/tests/unittests/test_distros/test_hostname.py
+++ b/tests/unittests/test_distros/test_hostname.py
@@ -1,4 +1,4 @@
-from mocker import MockerTestCase
+import unittest
from cloudinit.distros.parsers import hostname
@@ -12,7 +12,7 @@ blahblah
BASE_HOSTNAME = BASE_HOSTNAME.strip()
-class TestHostnameHelper(MockerTestCase):
+class TestHostnameHelper(unittest.TestCase):
def test_parse_same(self):
hn = hostname.HostnameConf(BASE_HOSTNAME)
self.assertEquals(str(hn).strip(), BASE_HOSTNAME)
diff --git a/tests/unittests/test_distros/test_hosts.py b/tests/unittests/test_distros/test_hosts.py
index 687a0dab..fc701eaa 100644
--- a/tests/unittests/test_distros/test_hosts.py
+++ b/tests/unittests/test_distros/test_hosts.py
@@ -1,4 +1,4 @@
-from mocker import MockerTestCase
+import unittest
from cloudinit.distros.parsers import hosts
@@ -14,7 +14,7 @@ BASE_ETC = '''
BASE_ETC = BASE_ETC.strip()
-class TestHostsHelper(MockerTestCase):
+class TestHostsHelper(unittest.TestCase):
def test_parse(self):
eh = hosts.HostsConf(BASE_ETC)
self.assertEquals(eh.get_entry('127.0.0.1'), [['localhost']])
diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
index 193338e8..2c2a424d 100644
--- a/tests/unittests/test_distros/test_netconfig.py
+++ b/tests/unittests/test_distros/test_netconfig.py
@@ -1,8 +1,16 @@
-from mocker import MockerTestCase
+import os
-import mocker
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
-import os
+from six import StringIO
+from ..helpers import TestCase
from cloudinit import distros
from cloudinit import helpers
@@ -11,8 +19,6 @@ from cloudinit import util
from cloudinit.distros.parsers.sys_conf import SysConf
-from StringIO import StringIO
-
BASE_NET_CFG = '''
auto lo
@@ -74,7 +80,7 @@ class WriteBuffer(object):
return self.buffer.getvalue()
-class TestNetCfgDistro(MockerTestCase):
+class TestNetCfgDistro(TestCase):
def _get_distro(self, dname):
cls = distros.fetch(dname)
@@ -85,34 +91,29 @@ class TestNetCfgDistro(MockerTestCase):
def test_simple_write_ub(self):
ub_distro = self._get_distro('ubuntu')
- util_mock = self.mocker.replace(util.write_file,
- spec=False, passthrough=False)
- exists_mock = self.mocker.replace(os.path.isfile,
- spec=False, passthrough=False)
-
- exists_mock(mocker.ARGS)
- self.mocker.count(0, None)
- self.mocker.result(False)
-
- write_bufs = {}
-
- def replace_write(filename, content, mode=0644, omode="wb"):
- buf = WriteBuffer()
- buf.mode = mode
- buf.omode = omode
- buf.write(content)
- write_bufs[filename] = buf
-
- util_mock(mocker.ARGS)
- self.mocker.call(replace_write)
- self.mocker.replay()
- ub_distro.apply_network(BASE_NET_CFG, False)
-
- self.assertEquals(len(write_bufs), 1)
- self.assertIn('/etc/network/interfaces', write_bufs)
- write_buf = write_bufs['/etc/network/interfaces']
- self.assertEquals(str(write_buf).strip(), BASE_NET_CFG.strip())
- self.assertEquals(write_buf.mode, 0644)
+ with ExitStack() as mocks:
+ write_bufs = {}
+
+ def replace_write(filename, content, mode=0o644, omode="wb"):
+ buf = WriteBuffer()
+ buf.mode = mode
+ buf.omode = omode
+ buf.write(content)
+ write_bufs[filename] = buf
+
+ mocks.enter_context(
+ mock.patch.object(util, 'write_file', replace_write))
+ mocks.enter_context(
+ mock.patch.object(os.path, 'isfile', return_value=False))
+
+ ub_distro.apply_network(BASE_NET_CFG, False)
+
+ self.assertEquals(len(write_bufs), 1)
+ eni_name = '/etc/network/interfaces.d/50-cloud-init.cfg'
+ self.assertIn(eni_name, write_bufs)
+ write_buf = write_bufs[eni_name]
+ self.assertEquals(str(write_buf).strip(), BASE_NET_CFG.strip())
+ self.assertEquals(write_buf.mode, 0o644)
def assertCfgEquals(self, blob1, blob2):
b1 = dict(SysConf(blob1.strip().splitlines()))
@@ -127,53 +128,41 @@ class TestNetCfgDistro(MockerTestCase):
def test_simple_write_rh(self):
rh_distro = self._get_distro('rhel')
- write_mock = self.mocker.replace(util.write_file,
- spec=False, passthrough=False)
- load_mock = self.mocker.replace(util.load_file,
- spec=False, passthrough=False)
- exists_mock = self.mocker.replace(os.path.isfile,
- spec=False, passthrough=False)
write_bufs = {}
- def replace_write(filename, content, mode=0644, omode="wb"):
+ def replace_write(filename, content, mode=0o644, omode="wb"):
buf = WriteBuffer()
buf.mode = mode
buf.omode = omode
buf.write(content)
write_bufs[filename] = buf
- exists_mock(mocker.ARGS)
- self.mocker.count(0, None)
- self.mocker.result(False)
-
- load_mock(mocker.ARGS)
- self.mocker.count(0, None)
- self.mocker.result('')
-
- for _i in range(0, 3):
- write_mock(mocker.ARGS)
- self.mocker.call(replace_write)
-
- write_mock(mocker.ARGS)
- self.mocker.call(replace_write)
-
- self.mocker.replay()
- rh_distro.apply_network(BASE_NET_CFG, False)
-
- self.assertEquals(len(write_bufs), 4)
- self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo']
- expected_buf = '''
+ with ExitStack() as mocks:
+ mocks.enter_context(
+ mock.patch.object(util, 'write_file', replace_write))
+ mocks.enter_context(
+ mock.patch.object(util, 'load_file', return_value=''))
+ mocks.enter_context(
+ mock.patch.object(os.path, 'isfile', return_value=False))
+
+ rh_distro.apply_network(BASE_NET_CFG, False)
+
+ self.assertEquals(len(write_bufs), 4)
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo']
+ expected_buf = '''
DEVICE="lo"
ONBOOT=yes
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
- self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth0', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth0']
- expected_buf = '''
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth0',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth0']
+ expected_buf = '''
DEVICE="eth0"
BOOTPROTO="static"
NETMASK="255.255.255.0"
@@ -182,77 +171,66 @@ ONBOOT=yes
GATEWAY="192.168.1.254"
BROADCAST="192.168.1.0"
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
- self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth1', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth1']
- expected_buf = '''
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth1',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth1']
+ expected_buf = '''
DEVICE="eth1"
BOOTPROTO="dhcp"
ONBOOT=yes
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
- self.assertIn('/etc/sysconfig/network', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network']
- expected_buf = '''
+ self.assertIn('/etc/sysconfig/network', write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network']
+ expected_buf = '''
# Created by cloud-init v. 0.7
NETWORKING=yes
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
def test_write_ipv6_rhel(self):
rh_distro = self._get_distro('rhel')
- write_mock = self.mocker.replace(util.write_file,
- spec=False, passthrough=False)
- load_mock = self.mocker.replace(util.load_file,
- spec=False, passthrough=False)
- exists_mock = self.mocker.replace(os.path.isfile,
- spec=False, passthrough=False)
write_bufs = {}
- def replace_write(filename, content, mode=0644, omode="wb"):
+ def replace_write(filename, content, mode=0o644, omode="wb"):
buf = WriteBuffer()
buf.mode = mode
buf.omode = omode
buf.write(content)
write_bufs[filename] = buf
- exists_mock(mocker.ARGS)
- self.mocker.count(0, None)
- self.mocker.result(False)
-
- load_mock(mocker.ARGS)
- self.mocker.count(0, None)
- self.mocker.result('')
-
- for _i in range(0, 3):
- write_mock(mocker.ARGS)
- self.mocker.call(replace_write)
-
- write_mock(mocker.ARGS)
- self.mocker.call(replace_write)
-
- self.mocker.replay()
- rh_distro.apply_network(BASE_NET_CFG_IPV6, False)
-
- self.assertEquals(len(write_bufs), 4)
- self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo']
- expected_buf = '''
+ with ExitStack() as mocks:
+ mocks.enter_context(
+ mock.patch.object(util, 'write_file', replace_write))
+ mocks.enter_context(
+ mock.patch.object(util, 'load_file', return_value=''))
+ mocks.enter_context(
+ mock.patch.object(os.path, 'isfile', return_value=False))
+
+ rh_distro.apply_network(BASE_NET_CFG_IPV6, False)
+
+ self.assertEquals(len(write_bufs), 4)
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo']
+ expected_buf = '''
DEVICE="lo"
ONBOOT=yes
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
- self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth0', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth0']
- expected_buf = '''
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth0',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth0']
+ expected_buf = '''
DEVICE="eth0"
BOOTPROTO="static"
NETMASK="255.255.255.0"
@@ -264,11 +242,12 @@ IPV6INIT=yes
IPV6ADDR="2607:f0d0:1002:0011::2"
IPV6_DEFAULTGW="2607:f0d0:1002:0011::1"
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
- self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth1', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth1']
- expected_buf = '''
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
+ self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth1',
+ write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth1']
+ expected_buf = '''
DEVICE="eth1"
BOOTPROTO="static"
NETMASK="255.255.255.0"
@@ -280,38 +259,22 @@ IPV6INIT=yes
IPV6ADDR="2607:f0d0:1002:0011::3"
IPV6_DEFAULTGW="2607:f0d0:1002:0011::1"
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
- self.assertIn('/etc/sysconfig/network', write_bufs)
- write_buf = write_bufs['/etc/sysconfig/network']
- expected_buf = '''
+ self.assertIn('/etc/sysconfig/network', write_bufs)
+ write_buf = write_bufs['/etc/sysconfig/network']
+ expected_buf = '''
# Created by cloud-init v. 0.7
NETWORKING=yes
NETWORKING_IPV6=yes
IPV6_AUTOCONF=no
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
def test_simple_write_freebsd(self):
fbsd_distro = self._get_distro('freebsd')
- util_mock = self.mocker.replace(util.write_file,
- spec=False, passthrough=False)
- exists_mock = self.mocker.replace(os.path.isfile,
- spec=False, passthrough=False)
- load_mock = self.mocker.replace(util.load_file,
- spec=False, passthrough=False)
- subp_mock = self.mocker.replace(util.subp,
- spec=False, passthrough=False)
-
- subp_mock(['ifconfig', '-a'])
- self.mocker.count(0, None)
- self.mocker.result(('vtnet0', ''))
-
- exists_mock(mocker.ARGS)
- self.mocker.count(0, None)
- self.mocker.result(False)
write_bufs = {}
read_bufs = {
@@ -319,7 +282,7 @@ IPV6_AUTOCONF=no
'/etc/resolv.conf': '',
}
- def replace_write(filename, content, mode=0644, omode="wb"):
+ def replace_write(filename, content, mode=0o644, omode="wb"):
buf = WriteBuffer()
buf.mode = mode
buf.omode = omode
@@ -336,23 +299,24 @@ IPV6_AUTOCONF=no
return str(write_bufs[fname])
return read_bufs[fname]
- util_mock(mocker.ARGS)
- self.mocker.call(replace_write)
- self.mocker.count(0, None)
-
- load_mock(mocker.ARGS)
- self.mocker.call(replace_read)
- self.mocker.count(0, None)
-
- self.mocker.replay()
- fbsd_distro.apply_network(BASE_NET_CFG, False)
-
- self.assertIn('/etc/rc.conf', write_bufs)
- write_buf = write_bufs['/etc/rc.conf']
- expected_buf = '''
+ with ExitStack() as mocks:
+ mocks.enter_context(
+ mock.patch.object(util, 'subp', return_value=('vtnet0', '')))
+ mocks.enter_context(
+ mock.patch.object(os.path, 'exists', return_value=False))
+ mocks.enter_context(
+ mock.patch.object(util, 'write_file', replace_write))
+ mocks.enter_context(
+ mock.patch.object(util, 'load_file', replace_read))
+
+ fbsd_distro.apply_network(BASE_NET_CFG, False)
+
+ self.assertIn('/etc/rc.conf', write_bufs)
+ write_buf = write_bufs['/etc/rc.conf']
+ expected_buf = '''
ifconfig_vtnet0="192.168.1.5 netmask 255.255.255.0"
ifconfig_vtnet1="DHCP"
defaultrouter="192.168.1.254"
'''
- self.assertCfgEquals(expected_buf, str(write_buf))
- self.assertEquals(write_buf.mode, 0644)
+ self.assertCfgEquals(expected_buf, str(write_buf))
+ self.assertEquals(write_buf.mode, 0o644)
diff --git a/tests/unittests/test_distros/test_resolv.py b/tests/unittests/test_distros/test_resolv.py
index 6b6ff6aa..faaf5b7f 100644
--- a/tests/unittests/test_distros/test_resolv.py
+++ b/tests/unittests/test_distros/test_resolv.py
@@ -1,8 +1,7 @@
-from mocker import MockerTestCase
-
from cloudinit.distros.parsers import resolv_conf
import re
+from ..helpers import TestCase
BASE_RESOLVE = '''
@@ -14,7 +13,7 @@ nameserver 10.15.30.92
BASE_RESOLVE = BASE_RESOLVE.strip()
-class TestResolvHelper(MockerTestCase):
+class TestResolvHelper(TestCase):
def test_parse_same(self):
rp = resolv_conf.ResolvConf(BASE_RESOLVE)
rp_r = str(rp).strip()
diff --git a/tests/unittests/test_distros/test_sysconfig.py b/tests/unittests/test_distros/test_sysconfig.py
index 0c651407..03d89a10 100644
--- a/tests/unittests/test_distros/test_sysconfig.py
+++ b/tests/unittests/test_distros/test_sysconfig.py
@@ -1,14 +1,13 @@
-from mocker import MockerTestCase
-
import re
from cloudinit.distros.parsers.sys_conf import SysConf
+from ..helpers import TestCase
# Lots of good examples @
# http://content.hccfl.edu/pollock/AUnix1/SysconfigFilesDesc.txt
-class TestSysConfHelper(MockerTestCase):
+class TestSysConfHelper(TestCase):
# This function was added in 2.7, make it work for 2.6
def assertRegMatches(self, text, regexp):
regexp = re.compile(regexp)
diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py
index 50398c74..4525f487 100644
--- a/tests/unittests/test_distros/test_user_data_normalize.py
+++ b/tests/unittests/test_distros/test_user_data_normalize.py
@@ -1,21 +1,22 @@
-from mocker import MockerTestCase
-
from cloudinit import distros
from cloudinit import helpers
from cloudinit import settings
+from ..helpers import TestCase
+
+
bcfg = {
- 'name': 'bob',
- 'plain_text_passwd': 'ubuntu',
- 'home': "/home/ubuntu",
- 'shell': "/bin/bash",
- 'lock_passwd': True,
- 'gecos': "Ubuntu",
- 'groups': ["foo"]
+ 'name': 'bob',
+ 'plain_text_passwd': 'ubuntu',
+ 'home': "/home/ubuntu",
+ 'shell': "/bin/bash",
+ 'lock_passwd': True,
+ 'gecos': "Ubuntu",
+ 'groups': ["foo"]
}
-class TestUGNormalize(MockerTestCase):
+class TestUGNormalize(TestCase):
def _make_distro(self, dtype, def_user=None):
cfg = dict(settings.CFG_BUILTIN)
@@ -33,16 +34,11 @@ class TestUGNormalize(MockerTestCase):
def test_group_dict(self):
distro = self._make_distro('ubuntu')
g = {'groups': [
- {
- 'ubuntu': ['foo', 'bar'],
- 'bob': 'users',
- },
- 'cloud-users',
- {
- 'bob': 'users2',
- },
- ]
- }
+ {'ubuntu': ['foo', 'bar'],
+ 'bob': 'users'},
+ 'cloud-users',
+ {'bob': 'users2'}
+ ]}
(_users, groups) = self._norm(g, distro)
self.assertIn('ubuntu', groups)
ub_members = groups['ubuntu']
diff --git a/tests/unittests/test_ec2_util.py b/tests/unittests/test_ec2_util.py
index 84aa002e..99fc54be 100644
--- a/tests/unittests/test_ec2_util.py
+++ b/tests/unittests/test_ec2_util.py
@@ -3,7 +3,7 @@ from . import helpers
from cloudinit import ec2_utils as eu
from cloudinit import url_helper as uh
-import httpretty as hp
+hp = helpers.import_httpretty()
class TestEc2Util(helpers.HttprettyTestCase):
@@ -16,7 +16,7 @@ class TestEc2Util(helpers.HttprettyTestCase):
body='stuff',
status=200)
userdata = eu.get_instance_userdata(self.VERSION)
- self.assertEquals('stuff', userdata)
+ self.assertEquals('stuff', userdata.decode('utf-8'))
@hp.activate
def test_userdata_fetch_fail_not_found(self):
diff --git a/tests/unittests/test_filters/test_launch_index.py b/tests/unittests/test_filters/test_launch_index.py
index 2f4c2fda..95d24b9b 100644
--- a/tests/unittests/test_filters/test_launch_index.py
+++ b/tests/unittests/test_filters/test_launch_index.py
@@ -2,7 +2,7 @@ import copy
from .. import helpers
-import itertools
+from six.moves import filterfalse
from cloudinit.filters import launch_index
from cloudinit import user_data as ud
@@ -36,11 +36,9 @@ class TestLaunchFilter(helpers.ResourceUsingTestCase):
return False
# Do some basic payload checking
msg1_msgs = [m for m in msg1.walk()]
- msg1_msgs = [m for m in
- itertools.ifilterfalse(ud.is_skippable, msg1_msgs)]
+ msg1_msgs = [m for m in filterfalse(ud.is_skippable, msg1_msgs)]
msg2_msgs = [m for m in msg2.walk()]
- msg2_msgs = [m for m in
- itertools.ifilterfalse(ud.is_skippable, msg2_msgs)]
+ msg2_msgs = [m for m in filterfalse(ud.is_skippable, msg2_msgs)]
for i in range(0, len(msg2_msgs)):
m1_msg = msg1_msgs[i]
m2_msg = msg2_msgs[i]
diff --git a/tests/unittests/test_handler/test_handler_apt_configure.py b/tests/unittests/test_handler/test_handler_apt_configure.py
index 203dd2aa..1ed185ca 100644
--- a/tests/unittests/test_handler/test_handler_apt_configure.py
+++ b/tests/unittests/test_handler/test_handler_apt_configure.py
@@ -1,27 +1,30 @@
-from mocker import MockerTestCase
-
from cloudinit import util
from cloudinit.config import cc_apt_configure
+from ..helpers import TestCase
import os
import re
+import shutil
+import tempfile
+
+def load_tfile_or_url(*args, **kwargs):
+ return(util.decode_binary(util.read_file_or_url(*args, **kwargs).contents))
-class TestAptProxyConfig(MockerTestCase):
+
+class TestAptProxyConfig(TestCase):
def setUp(self):
super(TestAptProxyConfig, self).setUp()
- self.tmp = self.makeDir()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
self.pfile = os.path.join(self.tmp, "proxy.cfg")
self.cfile = os.path.join(self.tmp, "config.cfg")
def _search_apt_config(self, contents, ptype, value):
- print(
- r"acquire::%s::proxy\s+[\"']%s[\"'];\n" % (ptype, value),
- contents, "flags=re.IGNORECASE")
- return(re.search(
+ return re.search(
r"acquire::%s::proxy\s+[\"']%s[\"'];\n" % (ptype, value),
- contents, flags=re.IGNORECASE))
+ contents, flags=re.IGNORECASE)
def test_apt_proxy_written(self):
cfg = {'apt_proxy': 'myproxy'}
@@ -30,7 +33,7 @@ class TestAptProxyConfig(MockerTestCase):
self.assertTrue(os.path.isfile(self.pfile))
self.assertFalse(os.path.isfile(self.cfile))
- contents = str(util.read_file_or_url(self.pfile))
+ contents = load_tfile_or_url(self.pfile)
self.assertTrue(self._search_apt_config(contents, "http", "myproxy"))
def test_apt_http_proxy_written(self):
@@ -40,7 +43,7 @@ class TestAptProxyConfig(MockerTestCase):
self.assertTrue(os.path.isfile(self.pfile))
self.assertFalse(os.path.isfile(self.cfile))
- contents = str(util.read_file_or_url(self.pfile))
+ contents = load_tfile_or_url(self.pfile)
self.assertTrue(self._search_apt_config(contents, "http", "myproxy"))
def test_apt_all_proxy_written(self):
@@ -58,9 +61,9 @@ class TestAptProxyConfig(MockerTestCase):
self.assertTrue(os.path.isfile(self.pfile))
self.assertFalse(os.path.isfile(self.cfile))
- contents = str(util.read_file_or_url(self.pfile))
+ contents = load_tfile_or_url(self.pfile)
- for ptype, pval in values.iteritems():
+ for ptype, pval in values.items():
self.assertTrue(self._search_apt_config(contents, ptype, pval))
def test_proxy_deleted(self):
@@ -74,7 +77,7 @@ class TestAptProxyConfig(MockerTestCase):
cc_apt_configure.apply_apt_config({'apt_proxy': "foo"},
self.pfile, self.cfile)
self.assertTrue(os.path.isfile(self.pfile))
- contents = str(util.read_file_or_url(self.pfile))
+ contents = load_tfile_or_url(self.pfile)
self.assertTrue(self._search_apt_config(contents, "http", "foo"))
def test_config_written(self):
@@ -86,14 +89,14 @@ class TestAptProxyConfig(MockerTestCase):
self.assertTrue(os.path.isfile(self.cfile))
self.assertFalse(os.path.isfile(self.pfile))
- self.assertEqual(str(util.read_file_or_url(self.cfile)), payload)
+ self.assertEqual(load_tfile_or_url(self.cfile), payload)
def test_config_replaced(self):
util.write_file(self.pfile, "content doesnt matter")
cc_apt_configure.apply_apt_config({'apt_config': "foo"},
self.pfile, self.cfile)
self.assertTrue(os.path.isfile(self.cfile))
- self.assertEqual(str(util.read_file_or_url(self.cfile)), "foo")
+ self.assertEqual(load_tfile_or_url(self.cfile), "foo")
def test_config_deleted(self):
# if no 'apt_config' is provided, delete any previously written file
diff --git a/tests/unittests/test_handler/test_handler_ca_certs.py b/tests/unittests/test_handler/test_handler_ca_certs.py
index 0558023a..a6b9c0fd 100644
--- a/tests/unittests/test_handler/test_handler_ca_certs.py
+++ b/tests/unittests/test_handler/test_handler_ca_certs.py
@@ -1,15 +1,26 @@
-from mocker import MockerTestCase
-
from cloudinit import cloud
from cloudinit import helpers
from cloudinit import util
from cloudinit.config import cc_ca_certs
+from ..helpers import TestCase
import logging
+import shutil
+import tempfile
+import unittest
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
-class TestNoConfig(MockerTestCase):
+class TestNoConfig(unittest.TestCase):
def setUp(self):
super(TestNoConfig, self).setUp()
self.name = "ca-certs"
@@ -22,15 +33,20 @@ class TestNoConfig(MockerTestCase):
Test that nothing is done if no ca-certs configuration is provided.
"""
config = util.get_builtin_cfg()
- self.mocker.replace(util.write_file, passthrough=False)
- self.mocker.replace(cc_ca_certs.update_ca_certs, passthrough=False)
- self.mocker.replay()
+ with ExitStack() as mocks:
+ util_mock = mocks.enter_context(
+ mock.patch.object(util, 'write_file'))
+ certs_mock = mocks.enter_context(
+ mock.patch.object(cc_ca_certs, 'update_ca_certs'))
- cc_ca_certs.handle(self.name, config, self.cloud_init, self.log,
- self.args)
+ cc_ca_certs.handle(self.name, config, self.cloud_init, self.log,
+ self.args)
+ self.assertEqual(util_mock.call_count, 0)
+ self.assertEqual(certs_mock.call_count, 0)
-class TestConfig(MockerTestCase):
+
+class TestConfig(TestCase):
def setUp(self):
super(TestConfig, self).setUp()
self.name = "ca-certs"
@@ -39,16 +55,16 @@ class TestConfig(MockerTestCase):
self.log = logging.getLogger("TestNoConfig")
self.args = []
- # Mock out the functions that actually modify the system
- self.mock_add = self.mocker.replace(cc_ca_certs.add_ca_certs,
- passthrough=False)
- self.mock_update = self.mocker.replace(cc_ca_certs.update_ca_certs,
- passthrough=False)
- self.mock_remove = self.mocker.replace(
- cc_ca_certs.remove_default_ca_certs, passthrough=False)
+ self.mocks = ExitStack()
+ self.addCleanup(self.mocks.close)
- # Order must be correct
- self.mocker.order()
+ # Mock out the functions that actually modify the system
+ self.mock_add = self.mocks.enter_context(
+ mock.patch.object(cc_ca_certs, 'add_ca_certs'))
+ self.mock_update = self.mocks.enter_context(
+ mock.patch.object(cc_ca_certs, 'update_ca_certs'))
+ self.mock_remove = self.mocks.enter_context(
+ mock.patch.object(cc_ca_certs, 'remove_default_ca_certs'))
def test_no_trusted_list(self):
"""
@@ -57,86 +73,88 @@ class TestConfig(MockerTestCase):
"""
config = {"ca-certs": {}}
- # No functions should be called
- self.mock_update()
- self.mocker.replay()
-
cc_ca_certs.handle(self.name, config, self.cloud, self.log, self.args)
+ self.assertEqual(self.mock_add.call_count, 0)
+ self.assertEqual(self.mock_update.call_count, 1)
+ self.assertEqual(self.mock_remove.call_count, 0)
+
def test_empty_trusted_list(self):
"""Test that no certificate are written if 'trusted' list is empty."""
config = {"ca-certs": {"trusted": []}}
- # No functions should be called
- self.mock_update()
- self.mocker.replay()
-
cc_ca_certs.handle(self.name, config, self.cloud, self.log, self.args)
+ self.assertEqual(self.mock_add.call_count, 0)
+ self.assertEqual(self.mock_update.call_count, 1)
+ self.assertEqual(self.mock_remove.call_count, 0)
+
def test_single_trusted(self):
"""Test that a single cert gets passed to add_ca_certs."""
config = {"ca-certs": {"trusted": ["CERT1"]}}
- self.mock_add(["CERT1"])
- self.mock_update()
- self.mocker.replay()
-
cc_ca_certs.handle(self.name, config, self.cloud, self.log, self.args)
+ self.mock_add.assert_called_once_with(['CERT1'])
+ self.assertEqual(self.mock_update.call_count, 1)
+ self.assertEqual(self.mock_remove.call_count, 0)
+
def test_multiple_trusted(self):
"""Test that multiple certs get passed to add_ca_certs."""
config = {"ca-certs": {"trusted": ["CERT1", "CERT2"]}}
- self.mock_add(["CERT1", "CERT2"])
- self.mock_update()
- self.mocker.replay()
-
cc_ca_certs.handle(self.name, config, self.cloud, self.log, self.args)
+ self.mock_add.assert_called_once_with(['CERT1', 'CERT2'])
+ self.assertEqual(self.mock_update.call_count, 1)
+ self.assertEqual(self.mock_remove.call_count, 0)
+
def test_remove_default_ca_certs(self):
"""Test remove_defaults works as expected."""
config = {"ca-certs": {"remove-defaults": True}}
- self.mock_remove()
- self.mock_update()
- self.mocker.replay()
-
cc_ca_certs.handle(self.name, config, self.cloud, self.log, self.args)
+ self.assertEqual(self.mock_add.call_count, 0)
+ self.assertEqual(self.mock_update.call_count, 1)
+ self.assertEqual(self.mock_remove.call_count, 1)
+
def test_no_remove_defaults_if_false(self):
"""Test remove_defaults is not called when config value is False."""
config = {"ca-certs": {"remove-defaults": False}}
- self.mock_update()
- self.mocker.replay()
-
cc_ca_certs.handle(self.name, config, self.cloud, self.log, self.args)
+ self.assertEqual(self.mock_add.call_count, 0)
+ self.assertEqual(self.mock_update.call_count, 1)
+ self.assertEqual(self.mock_remove.call_count, 0)
+
def test_correct_order_for_remove_then_add(self):
"""Test remove_defaults is not called when config value is False."""
config = {"ca-certs": {"remove-defaults": True, "trusted": ["CERT1"]}}
- self.mock_remove()
- self.mock_add(["CERT1"])
- self.mock_update()
- self.mocker.replay()
-
cc_ca_certs.handle(self.name, config, self.cloud, self.log, self.args)
+ self.mock_add.assert_called_once_with(['CERT1'])
+ self.assertEqual(self.mock_update.call_count, 1)
+ self.assertEqual(self.mock_remove.call_count, 1)
-class TestAddCaCerts(MockerTestCase):
+
+class TestAddCaCerts(TestCase):
def setUp(self):
super(TestAddCaCerts, self).setUp()
+ tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, tmpdir)
self.paths = helpers.Paths({
- 'cloud_dir': self.makeDir()
+ 'cloud_dir': tmpdir,
})
def test_no_certs_in_list(self):
"""Test that no certificate are written if not provided."""
- self.mocker.replace(util.write_file, passthrough=False)
- self.mocker.replay()
- cc_ca_certs.add_ca_certs([])
+ with mock.patch.object(util, 'write_file') as mockobj:
+ cc_ca_certs.add_ca_certs([])
+ self.assertEqual(mockobj.call_count, 0)
def test_single_cert_trailing_cr(self):
"""Test adding a single certificate to the trusted CAs
@@ -146,19 +164,21 @@ class TestAddCaCerts(MockerTestCase):
ca_certs_content = "line1\nline2\ncloud-init-ca-certs.crt\nline3\n"
expected = "line1\nline2\nline3\ncloud-init-ca-certs.crt\n"
- mock_write = self.mocker.replace(util.write_file, passthrough=False)
- mock_load = self.mocker.replace(util.load_file, passthrough=False)
-
- mock_write("/usr/share/ca-certificates/cloud-init-ca-certs.crt",
- cert, mode=0644)
-
- mock_load("/etc/ca-certificates.conf")
- self.mocker.result(ca_certs_content)
+ with ExitStack() as mocks:
+ mock_write = mocks.enter_context(
+ mock.patch.object(util, 'write_file'))
+ mock_load = mocks.enter_context(
+ mock.patch.object(util, 'load_file',
+ return_value=ca_certs_content))
- mock_write("/etc/ca-certificates.conf", expected, omode="wb")
- self.mocker.replay()
+ cc_ca_certs.add_ca_certs([cert])
- cc_ca_certs.add_ca_certs([cert])
+ mock_write.assert_has_calls([
+ mock.call("/usr/share/ca-certificates/cloud-init-ca-certs.crt",
+ cert, mode=0o644),
+ mock.call("/etc/ca-certificates.conf", expected, omode="wb"),
+ ])
+ mock_load.assert_called_once_with("/etc/ca-certificates.conf")
def test_single_cert_no_trailing_cr(self):
"""Test adding a single certificate to the trusted CAs
@@ -167,75 +187,89 @@ class TestAddCaCerts(MockerTestCase):
ca_certs_content = "line1\nline2\nline3"
- mock_write = self.mocker.replace(util.write_file, passthrough=False)
- mock_load = self.mocker.replace(util.load_file, passthrough=False)
+ with ExitStack() as mocks:
+ mock_write = mocks.enter_context(
+ mock.patch.object(util, 'write_file'))
+ mock_load = mocks.enter_context(
+ mock.patch.object(util, 'load_file',
+ return_value=ca_certs_content))
- mock_write("/usr/share/ca-certificates/cloud-init-ca-certs.crt",
- cert, mode=0644)
+ cc_ca_certs.add_ca_certs([cert])
- mock_load("/etc/ca-certificates.conf")
- self.mocker.result(ca_certs_content)
+ mock_write.assert_has_calls([
+ mock.call("/usr/share/ca-certificates/cloud-init-ca-certs.crt",
+ cert, mode=0o644),
+ mock.call("/etc/ca-certificates.conf",
+ "%s\n%s\n" % (ca_certs_content,
+ "cloud-init-ca-certs.crt"),
+ omode="wb"),
+ ])
- mock_write("/etc/ca-certificates.conf",
- "%s\n%s\n" % (ca_certs_content, "cloud-init-ca-certs.crt"),
- omode="wb")
- self.mocker.replay()
-
- cc_ca_certs.add_ca_certs([cert])
+ mock_load.assert_called_once_with("/etc/ca-certificates.conf")
def test_multiple_certs(self):
"""Test adding multiple certificates to the trusted CAs."""
certs = ["CERT1\nLINE2\nLINE3", "CERT2\nLINE2\nLINE3"]
expected_cert_file = "\n".join(certs)
-
- mock_write = self.mocker.replace(util.write_file, passthrough=False)
- mock_load = self.mocker.replace(util.load_file, passthrough=False)
-
- mock_write("/usr/share/ca-certificates/cloud-init-ca-certs.crt",
- expected_cert_file, mode=0644)
-
ca_certs_content = "line1\nline2\nline3"
- mock_load("/etc/ca-certificates.conf")
- self.mocker.result(ca_certs_content)
- out = "%s\n%s\n" % (ca_certs_content, "cloud-init-ca-certs.crt")
- mock_write("/etc/ca-certificates.conf", out, omode="wb")
+ with ExitStack() as mocks:
+ mock_write = mocks.enter_context(
+ mock.patch.object(util, 'write_file'))
+ mock_load = mocks.enter_context(
+ mock.patch.object(util, 'load_file',
+ return_value=ca_certs_content))
- self.mocker.replay()
+ cc_ca_certs.add_ca_certs(certs)
- cc_ca_certs.add_ca_certs(certs)
+ mock_write.assert_has_calls([
+ mock.call("/usr/share/ca-certificates/cloud-init-ca-certs.crt",
+ expected_cert_file, mode=0o644),
+ mock.call("/etc/ca-certificates.conf",
+ "%s\n%s\n" % (ca_certs_content,
+ "cloud-init-ca-certs.crt"),
+ omode='wb'),
+ ])
+ mock_load.assert_called_once_with("/etc/ca-certificates.conf")
-class TestUpdateCaCerts(MockerTestCase):
- def test_commands(self):
- mock_check_call = self.mocker.replace(util.subp,
- passthrough=False)
- mock_check_call(["update-ca-certificates"], capture=False)
- self.mocker.replay()
- cc_ca_certs.update_ca_certs()
+class TestUpdateCaCerts(unittest.TestCase):
+ def test_commands(self):
+ with mock.patch.object(util, 'subp') as mockobj:
+ cc_ca_certs.update_ca_certs()
+ mockobj.assert_called_once_with(
+ ["update-ca-certificates"], capture=False)
-class TestRemoveDefaultCaCerts(MockerTestCase):
+class TestRemoveDefaultCaCerts(TestCase):
def setUp(self):
super(TestRemoveDefaultCaCerts, self).setUp()
+ tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, tmpdir)
self.paths = helpers.Paths({
- 'cloud_dir': self.makeDir()
+ 'cloud_dir': tmpdir,
})
def test_commands(self):
- mock_delete_dir_contents = self.mocker.replace(
- util.delete_dir_contents, passthrough=False)
- mock_write = self.mocker.replace(util.write_file, passthrough=False)
- mock_subp = self.mocker.replace(util.subp,
- passthrough=False)
-
- mock_delete_dir_contents("/usr/share/ca-certificates/")
- mock_delete_dir_contents("/etc/ssl/certs/")
- mock_write("/etc/ca-certificates.conf", "", mode=0644)
- mock_subp(('debconf-set-selections', '-'),
- "ca-certificates ca-certificates/trust_new_crts select no")
- self.mocker.replay()
-
- cc_ca_certs.remove_default_ca_certs()
+ with ExitStack() as mocks:
+ mock_delete = mocks.enter_context(
+ mock.patch.object(util, 'delete_dir_contents'))
+ mock_write = mocks.enter_context(
+ mock.patch.object(util, 'write_file'))
+ mock_subp = mocks.enter_context(mock.patch.object(util, 'subp'))
+
+ cc_ca_certs.remove_default_ca_certs()
+
+ mock_delete.assert_has_calls([
+ mock.call("/usr/share/ca-certificates/"),
+ mock.call("/etc/ssl/certs/"),
+ ])
+
+ mock_write.assert_called_once_with(
+ "/etc/ca-certificates.conf", "", mode=0o644)
+
+ mock_subp.assert_called_once_with(
+ ('debconf-set-selections', '-'),
+ "ca-certificates ca-certificates/trust_new_crts select no")
diff --git a/tests/unittests/test_handler/test_handler_chef.py b/tests/unittests/test_handler/test_handler_chef.py
index ef1aa208..edad88cb 100644
--- a/tests/unittests/test_handler/test_handler_chef.py
+++ b/tests/unittests/test_handler/test_handler_chef.py
@@ -11,15 +11,21 @@ from cloudinit.sources import DataSourceNone
from .. import helpers as t_help
+import six
import logging
+import shutil
+import tempfile
LOG = logging.getLogger(__name__)
+CLIENT_TEMPL = os.path.sep.join(["templates", "chef_client.rb.tmpl"])
+
class TestChef(t_help.FilesystemMockingTestCase):
def setUp(self):
super(TestChef, self).setUp()
- self.tmp = self.makeDir(prefix="unittest_")
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def fetch_cloud(self, distro_kind):
cls = distros.fetch(distro_kind)
@@ -37,9 +43,13 @@ class TestChef(t_help.FilesystemMockingTestCase):
for d in cc_chef.CHEF_DIRS:
self.assertFalse(os.path.isdir(d))
+ @t_help.skipIf(not os.path.isfile(CLIENT_TEMPL),
+ CLIENT_TEMPL + " is not available")
def test_basic_config(self):
- # This should create a file of the format...
"""
+ test basic config looks sane
+
+ # This should create a file of the format...
# Created by cloud-init v. 0.7.6 on Sat, 11 Oct 2014 23:57:21 +0000
log_level :info
ssl_verify_mode :verify_none
@@ -74,7 +84,7 @@ class TestChef(t_help.FilesystemMockingTestCase):
for k, v in cfg['chef'].items():
self.assertIn(v, c)
for k, v in cc_chef.CHEF_RB_TPL_DEFAULTS.items():
- if isinstance(v, basestring):
+ if isinstance(v, six.string_types):
self.assertIn(v, c)
c = util.load_file(cc_chef.CHEF_FB_PATH)
self.assertEqual({}, json.loads(c))
@@ -101,6 +111,8 @@ class TestChef(t_help.FilesystemMockingTestCase):
'c': 'd',
}, json.loads(c))
+ @t_help.skipIf(not os.path.isfile(CLIENT_TEMPL),
+ CLIENT_TEMPL + " is not available")
def test_template_deletes(self):
tpl_file = util.load_file('templates/chef_client.rb.tmpl')
self.patchUtils(self.tmp)
diff --git a/tests/unittests/test_handler/test_handler_debug.py b/tests/unittests/test_handler/test_handler_debug.py
index 8891ca04..80708d7b 100644
--- a/tests/unittests/test_handler/test_handler_debug.py
+++ b/tests/unittests/test_handler/test_handler_debug.py
@@ -26,6 +26,8 @@ from cloudinit.sources import DataSourceNone
from .. import helpers as t_help
import logging
+import shutil
+import tempfile
LOG = logging.getLogger(__name__)
@@ -33,7 +35,8 @@ LOG = logging.getLogger(__name__)
class TestDebug(t_help.FilesystemMockingTestCase):
def setUp(self):
super(TestDebug, self).setUp()
- self.new_root = self.makeDir(prefix="unittest_")
+ self.new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.new_root)
def _get_cloud(self, distro, metadata=None):
self.patchUtils(self.new_root)
diff --git a/tests/unittests/test_handler/test_handler_disk_setup.py b/tests/unittests/test_handler/test_handler_disk_setup.py
new file mode 100644
index 00000000..ddef8d48
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_disk_setup.py
@@ -0,0 +1,30 @@
+from cloudinit.config import cc_disk_setup
+from ..helpers import ExitStack, mock, TestCase
+
+
+class TestIsDiskUsed(TestCase):
+
+ def setUp(self):
+ super(TestIsDiskUsed, self).setUp()
+ self.patches = ExitStack()
+ mod_name = 'cloudinit.config.cc_disk_setup'
+ self.enumerate_disk = self.patches.enter_context(
+ mock.patch('{0}.enumerate_disk'.format(mod_name)))
+ self.check_fs = self.patches.enter_context(
+ mock.patch('{0}.check_fs'.format(mod_name)))
+
+ def test_multiple_child_nodes_returns_true(self):
+ self.enumerate_disk.return_value = (mock.MagicMock() for _ in range(2))
+ self.check_fs.return_value = (mock.MagicMock(), None, mock.MagicMock())
+ self.assertTrue(cc_disk_setup.is_disk_used(mock.MagicMock()))
+
+ def test_valid_filesystem_returns_true(self):
+ self.enumerate_disk.return_value = (mock.MagicMock() for _ in range(1))
+ self.check_fs.return_value = (
+ mock.MagicMock(), 'ext4', mock.MagicMock())
+ self.assertTrue(cc_disk_setup.is_disk_used(mock.MagicMock()))
+
+ def test_one_child_nodes_and_no_fs_returns_false(self):
+ self.enumerate_disk.return_value = (mock.MagicMock() for _ in range(1))
+ self.check_fs.return_value = (mock.MagicMock(), None, mock.MagicMock())
+ self.assertFalse(cc_disk_setup.is_disk_used(mock.MagicMock()))
diff --git a/tests/unittests/test_handler/test_handler_growpart.py b/tests/unittests/test_handler/test_handler_growpart.py
index 5d0636d1..bef0d80d 100644
--- a/tests/unittests/test_handler/test_handler_growpart.py
+++ b/tests/unittests/test_handler/test_handler_growpart.py
@@ -1,14 +1,23 @@
-from mocker import MockerTestCase
-
from cloudinit import cloud
from cloudinit import util
from cloudinit.config import cc_growpart
+from ..helpers import TestCase
import errno
import logging
import os
import re
+import unittest
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+try:
+ from contextlib import ExitStack
+except ImportError:
+ from contextlib2 import ExitStack
# growpart:
# mode: auto # off, on, auto, 'growpart'
@@ -42,7 +51,7 @@ growpart disk partition
"""
-class TestDisabled(MockerTestCase):
+class TestDisabled(unittest.TestCase):
def setUp(self):
super(TestDisabled, self).setUp()
self.name = "growpart"
@@ -57,14 +66,14 @@ class TestDisabled(MockerTestCase):
# this really only verifies that resizer_factory isn't called
config = {'growpart': {'mode': 'off'}}
- self.mocker.replace(cc_growpart.resizer_factory,
- passthrough=False)
- self.mocker.replay()
- self.handle(self.name, config, self.cloud_init, self.log, self.args)
+ with mock.patch.object(cc_growpart, 'resizer_factory') as mockobj:
+ self.handle(self.name, config, self.cloud_init, self.log,
+ self.args)
+ self.assertEqual(mockobj.call_count, 0)
-class TestConfig(MockerTestCase):
+class TestConfig(TestCase):
def setUp(self):
super(TestConfig, self).setUp()
self.name = "growpart"
@@ -77,75 +86,76 @@ class TestConfig(MockerTestCase):
self.cloud_init = None
self.handle = cc_growpart.handle
- # Order must be correct
- self.mocker.order()
-
def test_no_resizers_auto_is_fine(self):
- subp = self.mocker.replace(util.subp, passthrough=False)
- subp(['growpart', '--help'], env={'LANG': 'C'})
- self.mocker.result((HELP_GROWPART_NO_RESIZE, ""))
- self.mocker.replay()
+ with mock.patch.object(
+ util, 'subp',
+ return_value=(HELP_GROWPART_NO_RESIZE, "")) as mockobj:
- config = {'growpart': {'mode': 'auto'}}
- self.handle(self.name, config, self.cloud_init, self.log, self.args)
+ config = {'growpart': {'mode': 'auto'}}
+ self.handle(self.name, config, self.cloud_init, self.log,
+ self.args)
+
+ mockobj.assert_called_once_with(
+ ['growpart', '--help'], env={'LANG': 'C'})
def test_no_resizers_mode_growpart_is_exception(self):
- subp = self.mocker.replace(util.subp, passthrough=False)
- subp(['growpart', '--help'], env={'LANG': 'C'})
- self.mocker.result((HELP_GROWPART_NO_RESIZE, ""))
- self.mocker.replay()
+ with mock.patch.object(
+ util, 'subp',
+ return_value=(HELP_GROWPART_NO_RESIZE, "")) as mockobj:
+ config = {'growpart': {'mode': "growpart"}}
+ self.assertRaises(
+ ValueError, self.handle, self.name, config,
+ self.cloud_init, self.log, self.args)
- config = {'growpart': {'mode': "growpart"}}
- self.assertRaises(ValueError, self.handle, self.name, config,
- self.cloud_init, self.log, self.args)
+ mockobj.assert_called_once_with(
+ ['growpart', '--help'], env={'LANG': 'C'})
def test_mode_auto_prefers_growpart(self):
- subp = self.mocker.replace(util.subp, passthrough=False)
- subp(['growpart', '--help'], env={'LANG': 'C'})
- self.mocker.result((HELP_GROWPART_RESIZE, ""))
- self.mocker.replay()
+ with mock.patch.object(
+ util, 'subp',
+ return_value=(HELP_GROWPART_RESIZE, "")) as mockobj:
+ ret = cc_growpart.resizer_factory(mode="auto")
+ self.assertIsInstance(ret, cc_growpart.ResizeGrowPart)
- ret = cc_growpart.resizer_factory(mode="auto")
- self.assertTrue(isinstance(ret, cc_growpart.ResizeGrowPart))
+ mockobj.assert_called_once_with(
+ ['growpart', '--help'], env={'LANG': 'C'})
def test_handle_with_no_growpart_entry(self):
# if no 'growpart' entry in config, then mode=auto should be used
myresizer = object()
+ retval = (("/", cc_growpart.RESIZE.CHANGED, "my-message",),)
+
+ with ExitStack() as mocks:
+ factory = mocks.enter_context(
+ mock.patch.object(cc_growpart, 'resizer_factory',
+ return_value=myresizer))
+ rsdevs = mocks.enter_context(
+ mock.patch.object(cc_growpart, 'resize_devices',
+ return_value=retval))
+ mocks.enter_context(
+ mock.patch.object(cc_growpart, 'RESIZERS',
+ (('mysizer', object),)
+ ))
- factory = self.mocker.replace(cc_growpart.resizer_factory,
- passthrough=False)
- rsdevs = self.mocker.replace(cc_growpart.resize_devices,
- passthrough=False)
- factory("auto")
- self.mocker.result(myresizer)
- rsdevs(myresizer, ["/"])
- self.mocker.result((("/", cc_growpart.RESIZE.CHANGED, "my-message",),))
- self.mocker.replay()
-
- try:
- orig_resizers = cc_growpart.RESIZERS
- cc_growpart.RESIZERS = (('mysizer', object),)
self.handle(self.name, {}, self.cloud_init, self.log, self.args)
- finally:
- cc_growpart.RESIZERS = orig_resizers
+ factory.assert_called_once_with('auto')
+ rsdevs.assert_called_once_with(myresizer, ['/'])
-class TestResize(MockerTestCase):
+
+class TestResize(unittest.TestCase):
def setUp(self):
super(TestResize, self).setUp()
self.name = "growpart"
self.log = logging.getLogger("TestResize")
- # Order must be correct
- self.mocker.order()
-
def test_simple_devices(self):
# test simple device list
# this patches out devent2dev, os.stat, and device_part_info
# so in the end, doesn't test a lot
devs = ["/dev/XXda1", "/dev/YYda2"]
- devstat_ret = Bunch(st_mode=25008, st_ino=6078, st_dev=5L,
+ devstat_ret = Bunch(st_mode=25008, st_ino=6078, st_dev=5,
st_nlink=1, st_uid=0, st_gid=6, st_size=0,
st_atime=0, st_mtime=0, st_ctime=0)
enoent = ["/dev/NOENT"]
diff --git a/tests/unittests/test_handler/test_handler_locale.py b/tests/unittests/test_handler/test_handler_locale.py
index eb251636..de85eff6 100644
--- a/tests/unittests/test_handler/test_handler_locale.py
+++ b/tests/unittests/test_handler/test_handler_locale.py
@@ -29,9 +29,11 @@ from .. import helpers as t_help
from configobj import ConfigObj
-from StringIO import StringIO
+from six import BytesIO
import logging
+import shutil
+import tempfile
LOG = logging.getLogger(__name__)
@@ -39,7 +41,8 @@ LOG = logging.getLogger(__name__)
class TestLocale(t_help.FilesystemMockingTestCase):
def setUp(self):
super(TestLocale, self).setUp()
- self.new_root = self.makeDir(prefix="unittest_")
+ self.new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.new_root)
def _get_cloud(self, distro):
self.patchUtils(self.new_root)
@@ -59,6 +62,6 @@ class TestLocale(t_help.FilesystemMockingTestCase):
cc = self._get_cloud('sles')
cc_locale.handle('cc_locale', cfg, cc, LOG, [])
- contents = util.load_file('/etc/sysconfig/language')
- n_cfg = ConfigObj(StringIO(contents))
+ contents = util.load_file('/etc/sysconfig/language', decode=False)
+ n_cfg = ConfigObj(BytesIO(contents))
self.assertEquals({'RC_LANG': cfg['locale']}, dict(n_cfg))
diff --git a/tests/unittests/test_handler/test_handler_lxd.py b/tests/unittests/test_handler/test_handler_lxd.py
new file mode 100644
index 00000000..7ffa2a53
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_lxd.py
@@ -0,0 +1,75 @@
+from cloudinit.config import cc_lxd
+from cloudinit import (distros, helpers, cloud)
+from cloudinit.sources import DataSourceNoCloud
+from .. import helpers as t_help
+
+import logging
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+
+LOG = logging.getLogger(__name__)
+
+
+class TestLxd(t_help.TestCase):
+ lxd_cfg = {
+ 'lxd': {
+ 'init': {
+ 'network_address': '0.0.0.0',
+ 'storage_backend': 'zfs',
+ 'storage_pool': 'poolname',
+ }
+ }
+ }
+
+ def setUp(self):
+ super(TestLxd, self).setUp()
+
+ def _get_cloud(self, distro):
+ cls = distros.fetch(distro)
+ paths = helpers.Paths({})
+ d = cls(distro, {}, paths)
+ ds = DataSourceNoCloud.DataSourceNoCloud({}, d, paths)
+ cc = cloud.Cloud(ds, paths, {}, d, None)
+ return cc
+
+ @mock.patch("cloudinit.config.cc_lxd.util")
+ def test_lxd_init(self, mock_util):
+ cc = self._get_cloud('ubuntu')
+ mock_util.which.return_value = True
+ cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, LOG, [])
+ self.assertTrue(mock_util.which.called)
+ init_call = mock_util.subp.call_args_list[0][0][0]
+ self.assertEquals(init_call,
+ ['lxd', 'init', '--auto',
+ '--network-address=0.0.0.0',
+ '--storage-backend=zfs',
+ '--storage-pool=poolname'])
+
+ @mock.patch("cloudinit.config.cc_lxd.util")
+ def test_lxd_install(self, mock_util):
+ cc = self._get_cloud('ubuntu')
+ cc.distro = mock.MagicMock()
+ mock_util.which.return_value = None
+ cc_lxd.handle('cc_lxd', self.lxd_cfg, cc, LOG, [])
+ self.assertTrue(cc.distro.install_packages.called)
+ install_pkg = cc.distro.install_packages.call_args_list[0][0][0]
+ self.assertEquals(sorted(install_pkg), ['lxd', 'zfs'])
+
+ @mock.patch("cloudinit.config.cc_lxd.util")
+ def test_no_init_does_nothing(self, mock_util):
+ cc = self._get_cloud('ubuntu')
+ cc.distro = mock.MagicMock()
+ cc_lxd.handle('cc_lxd', {'lxd': {}}, cc, LOG, [])
+ self.assertFalse(cc.distro.install_packages.called)
+ self.assertFalse(mock_util.subp.called)
+
+ @mock.patch("cloudinit.config.cc_lxd.util")
+ def test_no_lxd_does_nothing(self, mock_util):
+ cc = self._get_cloud('ubuntu')
+ cc.distro = mock.MagicMock()
+ cc_lxd.handle('cc_lxd', {'package_update': True}, cc, LOG, [])
+ self.assertFalse(cc.distro.install_packages.called)
+ self.assertFalse(mock_util.subp.called)
diff --git a/tests/unittests/test_handler/test_handler_mounts.py b/tests/unittests/test_handler/test_handler_mounts.py
new file mode 100644
index 00000000..355674b2
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_mounts.py
@@ -0,0 +1,133 @@
+import os.path
+import shutil
+import tempfile
+
+from cloudinit.config import cc_mounts
+
+from .. import helpers as test_helpers
+
+try:
+ from unittest import mock
+except ImportError:
+ import mock
+
+
+class TestSanitizeDevname(test_helpers.FilesystemMockingTestCase):
+
+ def setUp(self):
+ super(TestSanitizeDevname, self).setUp()
+ self.new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.new_root)
+ self.patchOS(self.new_root)
+
+ def _touch(self, path):
+ path = os.path.join(self.new_root, path.lstrip('/'))
+ basedir = os.path.dirname(path)
+ if not os.path.exists(basedir):
+ os.makedirs(basedir)
+ open(path, 'a').close()
+
+ def _makedirs(self, directory):
+ directory = os.path.join(self.new_root, directory.lstrip('/'))
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+ def mock_existence_of_disk(self, disk_path):
+ self._touch(disk_path)
+ self._makedirs(os.path.join('/sys/block', disk_path.split('/')[-1]))
+
+ def mock_existence_of_partition(self, disk_path, partition_number):
+ self.mock_existence_of_disk(disk_path)
+ self._touch(disk_path + str(partition_number))
+ disk_name = disk_path.split('/')[-1]
+ self._makedirs(os.path.join('/sys/block',
+ disk_name,
+ disk_name + str(partition_number)))
+
+ def test_existent_full_disk_path_is_returned(self):
+ disk_path = '/dev/sda'
+ self.mock_existence_of_disk(disk_path)
+ self.assertEqual(disk_path,
+ cc_mounts.sanitize_devname(disk_path,
+ lambda x: None,
+ mock.Mock()))
+
+ def test_existent_disk_name_returns_full_path(self):
+ disk_name = 'sda'
+ disk_path = '/dev/' + disk_name
+ self.mock_existence_of_disk(disk_path)
+ self.assertEqual(disk_path,
+ cc_mounts.sanitize_devname(disk_name,
+ lambda x: None,
+ mock.Mock()))
+
+ def test_existent_meta_disk_is_returned(self):
+ actual_disk_path = '/dev/sda'
+ self.mock_existence_of_disk(actual_disk_path)
+ self.assertEqual(
+ actual_disk_path,
+ cc_mounts.sanitize_devname('ephemeral0',
+ lambda x: actual_disk_path,
+ mock.Mock()))
+
+ def test_existent_meta_partition_is_returned(self):
+ disk_name, partition_part = '/dev/sda', '1'
+ actual_partition_path = disk_name + partition_part
+ self.mock_existence_of_partition(disk_name, partition_part)
+ self.assertEqual(
+ actual_partition_path,
+ cc_mounts.sanitize_devname('ephemeral0.1',
+ lambda x: disk_name,
+ mock.Mock()))
+
+ def test_existent_meta_partition_with_p_is_returned(self):
+ disk_name, partition_part = '/dev/sda', 'p1'
+ actual_partition_path = disk_name + partition_part
+ self.mock_existence_of_partition(disk_name, partition_part)
+ self.assertEqual(
+ actual_partition_path,
+ cc_mounts.sanitize_devname('ephemeral0.1',
+ lambda x: disk_name,
+ mock.Mock()))
+
+ def test_first_partition_returned_if_existent_disk_is_partitioned(self):
+ disk_name, partition_part = '/dev/sda', '1'
+ actual_partition_path = disk_name + partition_part
+ self.mock_existence_of_partition(disk_name, partition_part)
+ self.assertEqual(
+ actual_partition_path,
+ cc_mounts.sanitize_devname('ephemeral0',
+ lambda x: disk_name,
+ mock.Mock()))
+
+ def test_nth_partition_returned_if_requested(self):
+ disk_name, partition_part = '/dev/sda', '3'
+ actual_partition_path = disk_name + partition_part
+ self.mock_existence_of_partition(disk_name, partition_part)
+ self.assertEqual(
+ actual_partition_path,
+ cc_mounts.sanitize_devname('ephemeral0.3',
+ lambda x: disk_name,
+ mock.Mock()))
+
+ def test_transformer_returning_none_returns_none(self):
+ self.assertIsNone(
+ cc_mounts.sanitize_devname(
+ 'ephemeral0', lambda x: None, mock.Mock()))
+
+ def test_missing_device_returns_none(self):
+ self.assertIsNone(
+ cc_mounts.sanitize_devname('/dev/sda', None, mock.Mock()))
+
+ def test_missing_sys_returns_none(self):
+ disk_path = '/dev/sda'
+ self._makedirs(disk_path)
+ self.assertIsNone(
+ cc_mounts.sanitize_devname(disk_path, None, mock.Mock()))
+
+ def test_existent_disk_but_missing_partition_returns_none(self):
+ disk_path = '/dev/sda'
+ self.mock_existence_of_disk(disk_path)
+ self.assertIsNone(
+ cc_mounts.sanitize_devname(
+ 'ephemeral0.1', lambda x: disk_path, mock.Mock()))
diff --git a/tests/unittests/test_handler/test_handler_power_state.py b/tests/unittests/test_handler/test_handler_power_state.py
index 2f86b8f8..04ce5687 100644
--- a/tests/unittests/test_handler/test_handler_power_state.py
+++ b/tests/unittests/test_handler/test_handler_power_state.py
@@ -1,6 +1,9 @@
+import sys
+
from cloudinit.config import cc_power_state_change as psc
from .. import helpers as t_help
+from ..helpers import mock
class TestLoadPowerState(t_help.TestCase):
@@ -9,12 +12,12 @@ class TestLoadPowerState(t_help.TestCase):
def test_no_config(self):
# completely empty config should mean do nothing
- (cmd, _timeout) = psc.load_power_state({})
+ (cmd, _timeout, _condition) = psc.load_power_state({})
self.assertEqual(cmd, None)
def test_irrelevant_config(self):
# no power_state field in config should return None for cmd
- (cmd, _timeout) = psc.load_power_state({'foo': 'bar'})
+ (cmd, _timeout, _condition) = psc.load_power_state({'foo': 'bar'})
self.assertEqual(cmd, None)
def test_invalid_mode(self):
@@ -53,23 +56,59 @@ class TestLoadPowerState(t_help.TestCase):
def test_no_message(self):
# if message is not present, then no argument should be passed for it
cfg = {'power_state': {'mode': 'poweroff'}}
- (cmd, _timeout) = psc.load_power_state(cfg)
+ (cmd, _timeout, _condition) = psc.load_power_state(cfg)
self.assertNotIn("", cmd)
check_lps_ret(psc.load_power_state(cfg))
self.assertTrue(len(cmd) == 3)
+ def test_condition_null_raises(self):
+ cfg = {'power_state': {'mode': 'poweroff', 'condition': None}}
+ self.assertRaises(TypeError, psc.load_power_state, cfg)
+
+ def test_condition_default_is_true(self):
+ cfg = {'power_state': {'mode': 'poweroff'}}
+ _cmd, _timeout, cond = psc.load_power_state(cfg)
+ self.assertEqual(cond, True)
+
+
+class TestCheckCondition(t_help.TestCase):
+ def cmd_with_exit(self, rc):
+ return([sys.executable, '-c', 'import sys; sys.exit(%s)' % rc])
+
+ def test_true_is_true(self):
+ self.assertEqual(psc.check_condition(True), True)
+
+ def test_false_is_false(self):
+ self.assertEqual(psc.check_condition(False), False)
+
+ def test_cmd_exit_zero_true(self):
+ self.assertEqual(psc.check_condition(self.cmd_with_exit(0)), True)
+
+ def test_cmd_exit_one_false(self):
+ self.assertEqual(psc.check_condition(self.cmd_with_exit(1)), False)
+
+ def test_cmd_exit_nonzero_warns(self):
+ mocklog = mock.Mock()
+ self.assertEqual(
+ psc.check_condition(self.cmd_with_exit(2), mocklog), False)
+ self.assertEqual(mocklog.warn.call_count, 1)
+
def check_lps_ret(psc_return, mode=None):
- if len(psc_return) != 2:
+ if len(psc_return) != 3:
raise TypeError("length returned = %d" % len(psc_return))
errs = []
cmd = psc_return[0]
timeout = psc_return[1]
+ condition = psc_return[2]
if 'shutdown' not in psc_return[0][0]:
errs.append("string 'shutdown' not in cmd")
+ if condition is None:
+ errs.append("condition was not returned")
+
if mode is not None:
opt = {'halt': '-H', 'poweroff': '-P', 'reboot': '-r'}[mode]
if opt not in psc_return[0]:
diff --git a/tests/unittests/test_handler/test_handler_rsyslog.py b/tests/unittests/test_handler/test_handler_rsyslog.py
new file mode 100644
index 00000000..b932165c
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_rsyslog.py
@@ -0,0 +1,174 @@
+import os
+import shutil
+import tempfile
+
+from cloudinit.config.cc_rsyslog import (
+ apply_rsyslog_changes, DEF_DIR, DEF_FILENAME, DEF_RELOAD, load_config,
+ parse_remotes_line, remotes_to_rsyslog_cfg)
+from cloudinit import util
+
+from .. import helpers as t_help
+
+
+class TestLoadConfig(t_help.TestCase):
+ def setUp(self):
+ super(TestLoadConfig, self).setUp()
+ self.basecfg = {
+ 'config_filename': DEF_FILENAME,
+ 'config_dir': DEF_DIR,
+ 'service_reload_command': DEF_RELOAD,
+ 'configs': [],
+ 'remotes': {},
+ }
+
+ def test_legacy_full(self):
+ found = load_config({
+ 'rsyslog': ['*.* @192.168.1.1'],
+ 'rsyslog_dir': "mydir",
+ 'rsyslog_filename': "myfilename"})
+ self.basecfg.update({
+ 'configs': ['*.* @192.168.1.1'],
+ 'config_dir': "mydir",
+ 'config_filename': 'myfilename',
+ 'service_reload_command': 'auto'}
+ )
+
+ self.assertEqual(found, self.basecfg)
+
+ def test_legacy_defaults(self):
+ found = load_config({
+ 'rsyslog': ['*.* @192.168.1.1']})
+ self.basecfg.update({
+ 'configs': ['*.* @192.168.1.1']})
+ self.assertEqual(found, self.basecfg)
+
+ def test_new_defaults(self):
+ self.assertEqual(load_config({}), self.basecfg)
+
+ def test_new_configs(self):
+ cfgs = ['*.* myhost', '*.* my2host']
+ self.basecfg.update({'configs': cfgs})
+ self.assertEqual(
+ load_config({'rsyslog': {'configs': cfgs}}),
+ self.basecfg)
+
+
+class TestApplyChanges(t_help.TestCase):
+ def setUp(self):
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
+
+ def test_simple(self):
+ cfgline = "*.* foohost"
+ changed = apply_rsyslog_changes(
+ configs=[cfgline], def_fname="foo.cfg", cfg_dir=self.tmp)
+
+ fname = os.path.join(self.tmp, "foo.cfg")
+ self.assertEqual([fname], changed)
+ self.assertEqual(
+ util.load_file(fname), cfgline + "\n")
+
+ def test_multiple_files(self):
+ configs = [
+ '*.* foohost',
+ {'content': 'abc', 'filename': 'my.cfg'},
+ {'content': 'filefoo-content',
+ 'filename': os.path.join(self.tmp, 'mydir/mycfg')},
+ ]
+
+ changed = apply_rsyslog_changes(
+ configs=configs, def_fname="default.cfg", cfg_dir=self.tmp)
+
+ expected = [
+ (os.path.join(self.tmp, "default.cfg"),
+ "*.* foohost\n"),
+ (os.path.join(self.tmp, "my.cfg"), "abc\n"),
+ (os.path.join(self.tmp, "mydir/mycfg"), "filefoo-content\n"),
+ ]
+ self.assertEqual([f[0] for f in expected], changed)
+ actual = []
+ for fname, _content in expected:
+ util.load_file(fname)
+ actual.append((fname, util.load_file(fname),))
+ self.assertEqual(expected, actual)
+
+ def test_repeat_def(self):
+ configs = ['*.* foohost', "*.warn otherhost"]
+
+ changed = apply_rsyslog_changes(
+ configs=configs, def_fname="default.cfg", cfg_dir=self.tmp)
+
+ fname = os.path.join(self.tmp, "default.cfg")
+ self.assertEqual([fname], changed)
+
+ expected_content = '\n'.join([c for c in configs]) + '\n'
+ found_content = util.load_file(fname)
+ self.assertEqual(expected_content, found_content)
+
+ def test_multiline_content(self):
+ configs = ['line1', 'line2\nline3\n']
+
+ apply_rsyslog_changes(
+ configs=configs, def_fname="default.cfg", cfg_dir=self.tmp)
+
+ fname = os.path.join(self.tmp, "default.cfg")
+ expected_content = '\n'.join([c for c in configs])
+ found_content = util.load_file(fname)
+ self.assertEqual(expected_content, found_content)
+
+
+class TestParseRemotesLine(t_help.TestCase):
+ def test_valid_port(self):
+ r = parse_remotes_line("foo:9")
+ self.assertEqual(9, r.port)
+
+ def test_invalid_port(self):
+ with self.assertRaises(ValueError):
+ parse_remotes_line("*.* foo:abc")
+
+ def test_valid_ipv6(self):
+ r = parse_remotes_line("*.* [::1]")
+ self.assertEqual("*.* @[::1]", str(r))
+
+ def test_valid_ipv6_with_port(self):
+ r = parse_remotes_line("*.* [::1]:100")
+ self.assertEqual(r.port, 100)
+ self.assertEqual(r.addr, "::1")
+ self.assertEqual("*.* @[::1]:100", str(r))
+
+ def test_invalid_multiple_colon(self):
+ with self.assertRaises(ValueError):
+ parse_remotes_line("*.* ::1:100")
+
+ def test_name_in_string(self):
+ r = parse_remotes_line("syslog.host", name="foobar")
+ self.assertEqual("*.* @syslog.host # foobar", str(r))
+
+
+class TestRemotesToSyslog(t_help.TestCase):
+ def test_simple(self):
+ # str rendered line must appear in remotes_to_ryslog_cfg return
+ mycfg = "*.* myhost"
+ myline = str(parse_remotes_line(mycfg, name="myname"))
+ r = remotes_to_rsyslog_cfg({'myname': mycfg})
+ lines = r.splitlines()
+ self.assertEqual(1, len(lines))
+ self.assertTrue(myline in r.splitlines())
+
+ def test_header_footer(self):
+ header = "#foo head"
+ footer = "#foo foot"
+ r = remotes_to_rsyslog_cfg(
+ {'myname': "*.* myhost"}, header=header, footer=footer)
+ lines = r.splitlines()
+ self.assertTrue(header, lines[0])
+ self.assertTrue(footer, lines[-1])
+
+ def test_with_empty_or_null(self):
+ mycfg = "*.* myhost"
+ myline = str(parse_remotes_line(mycfg, name="myname"))
+ r = remotes_to_rsyslog_cfg(
+ {'myname': mycfg, 'removed': None, 'removed2': ""})
+ lines = r.splitlines()
+ self.assertEqual(1, len(lines))
+ self.assertTrue(myline in r.splitlines())
diff --git a/tests/unittests/test_handler/test_handler_seed_random.py b/tests/unittests/test_handler/test_handler_seed_random.py
index 40481f16..98bc9b81 100644
--- a/tests/unittests/test_handler/test_handler_seed_random.py
+++ b/tests/unittests/test_handler/test_handler_seed_random.py
@@ -18,11 +18,10 @@
from cloudinit.config import cc_seed_random
-import base64
import gzip
import tempfile
-from StringIO import StringIO
+from six import BytesIO
from cloudinit import cloud
from cloudinit import distros
@@ -69,7 +68,7 @@ class TestRandomSeed(t_help.TestCase):
return
def _compress(self, text):
- contents = StringIO()
+ contents = BytesIO()
gz_fh = gzip.GzipFile(mode='wb', fileobj=contents)
gz_fh.write(text)
gz_fh.close()
@@ -96,7 +95,7 @@ class TestRandomSeed(t_help.TestCase):
self.assertEquals("tiny-tim-was-here", contents)
def test_append_random_unknown_encoding(self):
- data = self._compress("tiny-toe")
+ data = self._compress(b"tiny-toe")
cfg = {
'random_seed': {
'file': self._seed_file,
@@ -108,7 +107,7 @@ class TestRandomSeed(t_help.TestCase):
self._get_cloud('ubuntu'), LOG, [])
def test_append_random_gzip(self):
- data = self._compress("tiny-toe")
+ data = self._compress(b"tiny-toe")
cfg = {
'random_seed': {
'file': self._seed_file,
@@ -121,7 +120,7 @@ class TestRandomSeed(t_help.TestCase):
self.assertEquals("tiny-toe", contents)
def test_append_random_gz(self):
- data = self._compress("big-toe")
+ data = self._compress(b"big-toe")
cfg = {
'random_seed': {
'file': self._seed_file,
@@ -134,7 +133,7 @@ class TestRandomSeed(t_help.TestCase):
self.assertEquals("big-toe", contents)
def test_append_random_base64(self):
- data = base64.b64encode('bubbles')
+ data = util.b64e('bubbles')
cfg = {
'random_seed': {
'file': self._seed_file,
@@ -147,7 +146,7 @@ class TestRandomSeed(t_help.TestCase):
self.assertEquals("bubbles", contents)
def test_append_random_b64(self):
- data = base64.b64encode('kit-kat')
+ data = util.b64e('kit-kat')
cfg = {
'random_seed': {
'file': self._seed_file,
@@ -171,27 +170,30 @@ class TestRandomSeed(t_help.TestCase):
contents = util.load_file(self._seed_file)
self.assertEquals('tiny-tim-was-here-so-was-josh', contents)
- def test_seed_command_not_provided_pollinate_available(self):
+ def test_seed_command_provided_and_available(self):
c = self._get_cloud('ubuntu', {})
self.whichdata = {'pollinate': '/usr/bin/pollinate'}
- cc_seed_random.handle('test', {}, c, LOG, [])
+ cfg = {'random_seed': {'command': ['pollinate', '-q']}}
+ cc_seed_random.handle('test', cfg, c, LOG, [])
subp_args = [f['args'] for f in self.subp_called]
self.assertIn(['pollinate', '-q'], subp_args)
- def test_seed_command_not_provided_pollinate_not_available(self):
+ def test_seed_command_not_provided(self):
c = self._get_cloud('ubuntu', {})
self.whichdata = {}
cc_seed_random.handle('test', {}, c, LOG, [])
# subp should not have been called as which would say not available
- self.assertEquals(self.subp_called, list())
+ self.assertFalse(self.subp_called)
def test_unavailable_seed_command_and_required_raises_error(self):
c = self._get_cloud('ubuntu', {})
self.whichdata = {}
+ cfg = {'random_seed': {'command': ['THIS_NO_COMMAND'],
+ 'command_required': True}}
self.assertRaises(ValueError, cc_seed_random.handle,
- 'test', {'random_seed': {'command_required': True}}, c, LOG, [])
+ 'test', cfg, c, LOG, [])
def test_seed_command_and_required(self):
c = self._get_cloud('ubuntu', {})
diff --git a/tests/unittests/test_handler/test_handler_set_hostname.py b/tests/unittests/test_handler/test_handler_set_hostname.py
index e1530e30..d358b069 100644
--- a/tests/unittests/test_handler/test_handler_set_hostname.py
+++ b/tests/unittests/test_handler/test_handler_set_hostname.py
@@ -7,9 +7,11 @@ from cloudinit import util
from .. import helpers as t_help
+import shutil
+import tempfile
import logging
-from StringIO import StringIO
+from six import BytesIO
from configobj import ConfigObj
@@ -19,7 +21,8 @@ LOG = logging.getLogger(__name__)
class TestHostname(t_help.FilesystemMockingTestCase):
def setUp(self):
super(TestHostname, self).setUp()
- self.tmp = self.makeDir(prefix="unittest_")
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def _fetch_distro(self, kind):
cls = distros.fetch(kind)
@@ -38,8 +41,8 @@ class TestHostname(t_help.FilesystemMockingTestCase):
cc_set_hostname.handle('cc_set_hostname',
cfg, cc, LOG, [])
if not distro.uses_systemd():
- contents = util.load_file("/etc/sysconfig/network")
- n_cfg = ConfigObj(StringIO(contents))
+ contents = util.load_file("/etc/sysconfig/network", decode=False)
+ n_cfg = ConfigObj(BytesIO(contents))
self.assertEquals({'HOSTNAME': 'blah.blah.blah.yahoo.com'},
dict(n_cfg))
diff --git a/tests/unittests/test_handler/test_handler_snappy.py b/tests/unittests/test_handler/test_handler_snappy.py
new file mode 100644
index 00000000..8aeff53c
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_snappy.py
@@ -0,0 +1,305 @@
+from cloudinit.config.cc_snappy import (
+ makeop, get_package_ops, render_snap_op)
+from cloudinit import util
+from .. import helpers as t_help
+
+import os
+import shutil
+import tempfile
+import yaml
+
+ALLOWED = (dict, list, int, str)
+
+
+class TestInstallPackages(t_help.TestCase):
+ def setUp(self):
+ super(TestInstallPackages, self).setUp()
+ self.unapply = []
+
+ # by default 'which' has nothing in its path
+ self.apply_patches([(util, 'subp', self._subp)])
+ self.subp_called = []
+ self.snapcmds = []
+ self.tmp = tempfile.mkdtemp(prefix="TestInstallPackages")
+
+ def tearDown(self):
+ apply_patches([i for i in reversed(self.unapply)])
+ shutil.rmtree(self.tmp)
+
+ def apply_patches(self, patches):
+ ret = apply_patches(patches)
+ self.unapply += ret
+
+ def populate_tmp(self, files):
+ return t_help.populate_dir(self.tmp, files)
+
+ def _subp(self, *args, **kwargs):
+ # supports subp calling with cmd as args or kwargs
+ if 'args' not in kwargs:
+ kwargs['args'] = args[0]
+ self.subp_called.append(kwargs)
+ args = kwargs['args']
+ # here we basically parse the snappy command invoked
+ # and append to snapcmds a list of (mode, pkg, config)
+ if args[0:2] == ['snappy', 'config']:
+ if args[3] == "-":
+ config = kwargs.get('data', '')
+ else:
+ with open(args[3], "rb") as fp:
+ config = yaml.safe_load(fp.read())
+ self.snapcmds.append(['config', args[2], config])
+ elif args[0:2] == ['snappy', 'install']:
+ config = None
+ pkg = None
+ for arg in args[2:]:
+ if arg.startswith("-"):
+ continue
+ if not pkg:
+ pkg = arg
+ elif not config:
+ cfgfile = arg
+ if cfgfile == "-":
+ config = kwargs.get('data', '')
+ elif cfgfile:
+ with open(cfgfile, "rb") as fp:
+ config = yaml.safe_load(fp.read())
+ self.snapcmds.append(['install', pkg, config])
+
+ def test_package_ops_1(self):
+ ret = get_package_ops(
+ packages=['pkg1', 'pkg2', 'pkg3'],
+ configs={'pkg2': b'mycfg2'}, installed=[])
+ self.assertEqual(
+ ret, [makeop('install', 'pkg1', None, None),
+ makeop('install', 'pkg2', b'mycfg2', None),
+ makeop('install', 'pkg3', None, None)])
+
+ def test_package_ops_config_only(self):
+ ret = get_package_ops(
+ packages=None,
+ configs={'pkg2': b'mycfg2'}, installed=['pkg1', 'pkg2'])
+ self.assertEqual(
+ ret, [makeop('config', 'pkg2', b'mycfg2')])
+
+ def test_package_ops_install_and_config(self):
+ ret = get_package_ops(
+ packages=['pkg3', 'pkg2'],
+ configs={'pkg2': b'mycfg2', 'xinstalled': b'xcfg'},
+ installed=['xinstalled'])
+ self.assertEqual(
+ ret, [makeop('install', 'pkg3'),
+ makeop('install', 'pkg2', b'mycfg2'),
+ makeop('config', 'xinstalled', b'xcfg')])
+
+ def test_package_ops_install_long_config_short(self):
+ # a package can be installed by full name, but have config by short
+ cfg = {'k1': 'k2'}
+ ret = get_package_ops(
+ packages=['config-example.canonical'],
+ configs={'config-example': cfg}, installed=[])
+ self.assertEqual(
+ ret, [makeop('install', 'config-example.canonical', cfg)])
+
+ def test_package_ops_with_file(self):
+ self.populate_tmp(
+ {"snapf1.snap": b"foo1", "snapf1.config": b"snapf1cfg",
+ "snapf2.snap": b"foo2", "foo.bar": "ignored"})
+ ret = get_package_ops(
+ packages=['pkg1'], configs={}, installed=[], fspath=self.tmp)
+ self.assertEqual(
+ ret,
+ [makeop_tmpd(self.tmp, 'install', 'snapf1', path="snapf1.snap",
+ cfgfile="snapf1.config"),
+ makeop_tmpd(self.tmp, 'install', 'snapf2', path="snapf2.snap"),
+ makeop('install', 'pkg1')])
+
+ def test_package_ops_common_filename(self):
+ # fish package name from filename
+ # package names likely look like: pkgname.namespace_version_arch.snap
+
+ # find filenames
+ self.populate_tmp(
+ {"pkg-ws.smoser_0.3.4_all.snap": "pkg-ws-snapdata",
+ "pkg-ws.config": "pkg-ws-config",
+ "pkg1.smoser_1.2.3_all.snap": "pkg1.snapdata",
+ "pkg1.smoser.config": "pkg1.smoser.config-data",
+ "pkg1.config": "pkg1.config-data",
+ "pkg2.smoser_0.0_amd64.snap": "pkg2-snapdata",
+ "pkg2.smoser_0.0_amd64.config": "pkg2.config"})
+
+ ret = get_package_ops(
+ packages=[], configs={}, installed=[], fspath=self.tmp)
+ self.assertEqual(
+ ret,
+ [makeop_tmpd(self.tmp, 'install', 'pkg-ws.smoser',
+ path="pkg-ws.smoser_0.3.4_all.snap",
+ cfgfile="pkg-ws.config"),
+ makeop_tmpd(self.tmp, 'install', 'pkg1.smoser',
+ path="pkg1.smoser_1.2.3_all.snap",
+ cfgfile="pkg1.smoser.config"),
+ makeop_tmpd(self.tmp, 'install', 'pkg2.smoser',
+ path="pkg2.smoser_0.0_amd64.snap",
+ cfgfile="pkg2.smoser_0.0_amd64.config"),
+ ])
+
+ def test_package_ops_config_overrides_file(self):
+ # config data overrides local file .config
+ self.populate_tmp(
+ {"snapf1.snap": b"foo1", "snapf1.config": b"snapf1cfg"})
+ ret = get_package_ops(
+ packages=[], configs={'snapf1': 'snapf1cfg-config'},
+ installed=[], fspath=self.tmp)
+ self.assertEqual(
+ ret, [makeop_tmpd(self.tmp, 'install', 'snapf1',
+ path="snapf1.snap", config="snapf1cfg-config")])
+
+ def test_package_ops_namespacing(self):
+ cfgs = {
+ 'config-example': {'k1': 'v1'},
+ 'pkg1': {'p1': 'p2'},
+ 'ubuntu-core': {'c1': 'c2'},
+ 'notinstalled.smoser': {'s1': 's2'},
+ }
+ ret = get_package_ops(
+ packages=['config-example.canonical'], configs=cfgs,
+ installed=['config-example.smoser', 'pkg1.canonical',
+ 'ubuntu-core'])
+
+ expected_configs = [
+ makeop('config', 'pkg1', config=cfgs['pkg1']),
+ makeop('config', 'ubuntu-core', config=cfgs['ubuntu-core'])]
+ expected_installs = [
+ makeop('install', 'config-example.canonical',
+ config=cfgs['config-example'])]
+
+ installs = [i for i in ret if i['op'] == 'install']
+ configs = [c for c in ret if c['op'] == 'config']
+
+ self.assertEqual(installs, expected_installs)
+ # configs are not ordered
+ self.assertEqual(len(configs), len(expected_configs))
+ self.assertTrue(all(found in expected_configs for found in configs))
+
+ def test_render_op_localsnap(self):
+ self.populate_tmp({"snapf1.snap": b"foo1"})
+ op = makeop_tmpd(self.tmp, 'install', 'snapf1',
+ path='snapf1.snap')
+ render_snap_op(**op)
+ self.assertEqual(
+ self.snapcmds, [['install', op['path'], None]])
+
+ def test_render_op_localsnap_localconfig(self):
+ self.populate_tmp(
+ {"snapf1.snap": b"foo1", 'snapf1.config': b'snapf1cfg'})
+ op = makeop_tmpd(self.tmp, 'install', 'snapf1',
+ path='snapf1.snap', cfgfile='snapf1.config')
+ render_snap_op(**op)
+ self.assertEqual(
+ self.snapcmds, [['install', op['path'], 'snapf1cfg']])
+
+ def test_render_op_snap(self):
+ op = makeop('install', 'snapf1')
+ render_snap_op(**op)
+ self.assertEqual(
+ self.snapcmds, [['install', 'snapf1', None]])
+
+ def test_render_op_snap_config(self):
+ mycfg = {'key1': 'value1'}
+ name = "snapf1"
+ op = makeop('install', name, config=mycfg)
+ render_snap_op(**op)
+ self.assertEqual(
+ self.snapcmds, [['install', name, {'config': {name: mycfg}}]])
+
+ def test_render_op_config_bytes(self):
+ name = "snapf1"
+ mycfg = b'myconfig'
+ op = makeop('config', name, config=mycfg)
+ render_snap_op(**op)
+ self.assertEqual(
+ self.snapcmds, [['config', 'snapf1', {'config': {name: mycfg}}]])
+
+ def test_render_op_config_string(self):
+ name = 'snapf1'
+ mycfg = 'myconfig: foo\nhisconfig: bar\n'
+ op = makeop('config', name, config=mycfg)
+ render_snap_op(**op)
+ self.assertEqual(
+ self.snapcmds, [['config', 'snapf1', {'config': {name: mycfg}}]])
+
+ def test_render_op_config_dict(self):
+ # config entry for package can be a dict, not a string blob
+ mycfg = {'foo': 'bar'}
+ name = 'snapf1'
+ op = makeop('config', name, config=mycfg)
+ render_snap_op(**op)
+ # snapcmds is a list of 3-entry lists. data_found will be the
+ # blob of data in the file in 'snappy install --config=<file>'
+ data_found = self.snapcmds[0][2]
+ self.assertEqual(mycfg, data_found['config'][name])
+
+ def test_render_op_config_list(self):
+ # config entry for package can be a list, not a string blob
+ mycfg = ['foo', 'bar', 'wark', {'f1': 'b1'}]
+ name = "snapf1"
+ op = makeop('config', name, config=mycfg)
+ render_snap_op(**op)
+ data_found = self.snapcmds[0][2]
+ self.assertEqual(mycfg, data_found['config'][name])
+
+ def test_render_op_config_int(self):
+ # config entry for package can be a list, not a string blob
+ mycfg = 1
+ name = 'snapf1'
+ op = makeop('config', name, config=mycfg)
+ render_snap_op(**op)
+ data_found = self.snapcmds[0][2]
+ self.assertEqual(mycfg, data_found['config'][name])
+
+ def test_render_long_configs_short(self):
+ # install a namespaced package should have un-namespaced config
+ mycfg = {'k1': 'k2'}
+ name = 'snapf1'
+ op = makeop('install', name + ".smoser", config=mycfg)
+ render_snap_op(**op)
+ data_found = self.snapcmds[0][2]
+ self.assertEqual(mycfg, data_found['config'][name])
+
+ def test_render_does_not_pad_cfgfile(self):
+ # package_ops with cfgfile should not modify --file= content.
+ mydata = "foo1: bar1\nk: [l1, l2, l3]\n"
+ self.populate_tmp(
+ {"snapf1.snap": b"foo1", "snapf1.config": mydata.encode()})
+ ret = get_package_ops(
+ packages=[], configs={}, installed=[], fspath=self.tmp)
+ self.assertEqual(
+ ret,
+ [makeop_tmpd(self.tmp, 'install', 'snapf1', path="snapf1.snap",
+ cfgfile="snapf1.config")])
+
+ # now the op was ok, but test that render didn't mess it up.
+ render_snap_op(**ret[0])
+ data_found = self.snapcmds[0][2]
+ # the data found gets loaded in the snapcmd interpretation
+ # so this comparison is a bit lossy, but input to snappy config
+ # is expected to be yaml loadable, so it should be OK.
+ self.assertEqual(yaml.safe_load(mydata), data_found)
+
+
+def makeop_tmpd(tmpd, op, name, config=None, path=None, cfgfile=None):
+ if cfgfile:
+ cfgfile = os.path.sep.join([tmpd, cfgfile])
+ if path:
+ path = os.path.sep.join([tmpd, path])
+ return(makeop(op=op, name=name, config=config, path=path, cfgfile=cfgfile))
+
+
+def apply_patches(patches):
+ ret = []
+ for (ref, name, replace) in patches:
+ if replace is None:
+ continue
+ orig = getattr(ref, name)
+ setattr(ref, name, replace)
+ ret.append((ref, name, orig))
+ return ret
diff --git a/tests/unittests/test_handler/test_handler_timezone.py b/tests/unittests/test_handler/test_handler_timezone.py
index 874db340..e3df8759 100644
--- a/tests/unittests/test_handler/test_handler_timezone.py
+++ b/tests/unittests/test_handler/test_handler_timezone.py
@@ -29,8 +29,10 @@ from .. import helpers as t_help
from configobj import ConfigObj
-from StringIO import StringIO
+from six import BytesIO
+import shutil
+import tempfile
import logging
LOG = logging.getLogger(__name__)
@@ -39,7 +41,8 @@ LOG = logging.getLogger(__name__)
class TestTimezone(t_help.FilesystemMockingTestCase):
def setUp(self):
super(TestTimezone, self).setUp()
- self.new_root = self.makeDir(prefix="unittest_")
+ self.new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.new_root)
def _get_cloud(self, distro):
self.patchUtils(self.new_root)
@@ -67,8 +70,8 @@ class TestTimezone(t_help.FilesystemMockingTestCase):
cc_timezone.handle('cc_timezone', cfg, cc, LOG, [])
- contents = util.load_file('/etc/sysconfig/clock')
- n_cfg = ConfigObj(StringIO(contents))
+ contents = util.load_file('/etc/sysconfig/clock', decode=False)
+ n_cfg = ConfigObj(BytesIO(contents))
self.assertEquals({'TIMEZONE': cfg['timezone']}, dict(n_cfg))
contents = util.load_file('/etc/localtime')
diff --git a/tests/unittests/test_handler/test_handler_yum_add_repo.py b/tests/unittests/test_handler/test_handler_yum_add_repo.py
index 435c9787..3a8aa7c1 100644
--- a/tests/unittests/test_handler/test_handler_yum_add_repo.py
+++ b/tests/unittests/test_handler/test_handler_yum_add_repo.py
@@ -4,9 +4,11 @@ from cloudinit.config import cc_yum_add_repo
from .. import helpers
+import shutil
+import tempfile
import logging
-from StringIO import StringIO
+from six import BytesIO
import configobj
@@ -16,7 +18,8 @@ LOG = logging.getLogger(__name__)
class TestConfig(helpers.FilesystemMockingTestCase):
def setUp(self):
super(TestConfig, self).setUp()
- self.tmp = self.makeDir(prefix="unittest_")
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def test_bad_config(self):
cfg = {
@@ -52,8 +55,9 @@ class TestConfig(helpers.FilesystemMockingTestCase):
}
self.patchUtils(self.tmp)
cc_yum_add_repo.handle('yum_add_repo', cfg, None, LOG, [])
- contents = util.load_file("/etc/yum.repos.d/epel_testing.repo")
- contents = configobj.ConfigObj(StringIO(contents))
+ contents = util.load_file("/etc/yum.repos.d/epel_testing.repo",
+ decode=False)
+ contents = configobj.ConfigObj(BytesIO(contents))
expected = {
'epel_testing': {
'name': 'Extra Packages for Enterprise Linux 5 - Testing',
diff --git a/tests/unittests/test_merging.py b/tests/unittests/test_merging.py
index 07b610f7..976d8283 100644
--- a/tests/unittests/test_merging.py
+++ b/tests/unittests/test_merging.py
@@ -11,11 +11,13 @@ import glob
import os
import random
import re
+import six
import string
SOURCE_PAT = "source*.*yaml"
EXPECTED_PAT = "expected%s.yaml"
-TYPES = [long, int, dict, str, list, tuple, None]
+TYPES = [dict, str, list, tuple, None]
+TYPES.extend(six.integer_types)
def _old_mergedict(src, cand):
@@ -25,7 +27,7 @@ def _old_mergedict(src, cand):
Nested dictionaries are merged recursively.
"""
if isinstance(src, dict) and isinstance(cand, dict):
- for (k, v) in cand.iteritems():
+ for (k, v) in cand.items():
if k not in src:
src[k] = v
else:
@@ -42,8 +44,8 @@ def _old_mergemanydict(*args):
def _random_str(rand):
base = ''
- for _i in xrange(rand.randint(1, 2 ** 8)):
- base += rand.choice(string.letters + string.digits)
+ for _i in range(rand.randint(1, 2 ** 8)):
+ base += rand.choice(string.ascii_letters + string.digits)
return base
@@ -64,7 +66,7 @@ def _make_dict(current_depth, max_depth, rand):
if t in [dict, list, tuple]:
if t in [dict]:
amount = rand.randint(0, 5)
- keys = [_random_str(rand) for _i in xrange(0, amount)]
+ keys = [_random_str(rand) for _i in range(0, amount)]
base = {}
for k in keys:
try:
@@ -74,14 +76,14 @@ def _make_dict(current_depth, max_depth, rand):
elif t in [list, tuple]:
base = []
amount = rand.randint(0, 5)
- for _i in xrange(0, amount):
+ for _i in range(0, amount):
try:
base.append(_make_dict(current_depth + 1, max_depth, rand))
except _NoMoreException:
pass
if t in [tuple]:
base = tuple(base)
- elif t in [long, int]:
+ elif t in six.integer_types:
base = rand.randint(0, 2 ** 8)
elif t in [str]:
base = _random_str(rand)
diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
new file mode 100644
index 00000000..dfb31710
--- /dev/null
+++ b/tests/unittests/test_net.py
@@ -0,0 +1,127 @@
+from cloudinit import util
+from cloudinit import net
+from .helpers import TestCase
+
+import base64
+import copy
+import io
+import gzip
+import json
+import os
+
+DHCP_CONTENT_1 = """
+DEVICE='eth0'
+PROTO='dhcp'
+IPV4ADDR='192.168.122.89'
+IPV4BROADCAST='192.168.122.255'
+IPV4NETMASK='255.255.255.0'
+IPV4GATEWAY='192.168.122.1'
+IPV4DNS0='192.168.122.1'
+IPV4DNS1='0.0.0.0'
+HOSTNAME='foohost'
+DNSDOMAIN=''
+NISDOMAIN=''
+ROOTSERVER='192.168.122.1'
+ROOTPATH=''
+filename=''
+UPTIME='21'
+DHCPLEASETIME='3600'
+DOMAINSEARCH='foo.com'
+"""
+
+DHCP_EXPECTED_1 = {
+ 'name': 'eth0',
+ 'type': 'physical',
+ 'subnets': [{'broadcast': '192.168.122.255',
+ 'gateway': '192.168.122.1',
+ 'dns_search': ['foo.com'],
+ 'type': 'dhcp',
+ 'netmask': '255.255.255.0',
+ 'dns_nameservers': ['192.168.122.1']}],
+}
+
+
+STATIC_CONTENT_1 = """
+DEVICE='eth1'
+PROTO='static'
+IPV4ADDR='10.0.0.2'
+IPV4BROADCAST='10.0.0.255'
+IPV4NETMASK='255.255.255.0'
+IPV4GATEWAY='10.0.0.1'
+IPV4DNS0='10.0.1.1'
+IPV4DNS1='0.0.0.0'
+HOSTNAME='foohost'
+UPTIME='21'
+DHCPLEASETIME='3600'
+DOMAINSEARCH='foo.com'
+"""
+
+STATIC_EXPECTED_1 = {
+ 'name': 'eth1',
+ 'type': 'physical',
+ 'subnets': [{'broadcast': '10.0.0.255', 'gateway': '10.0.0.1',
+ 'dns_search': ['foo.com'], 'type': 'static',
+ 'netmask': '255.255.255.0',
+ 'dns_nameservers': ['10.0.1.1']}],
+}
+
+
+class TestNetConfigParsing(TestCase):
+ simple_cfg = {
+ 'config': [{"type": "physical", "name": "eth0",
+ "mac_address": "c0:d6:9f:2c:e8:80",
+ "subnets": [{"type": "dhcp"}]}]}
+
+ def test_klibc_convert_dhcp(self):
+ found = net._klibc_to_config_entry(DHCP_CONTENT_1)
+ self.assertEqual(found, ('eth0', DHCP_EXPECTED_1))
+
+ def test_klibc_convert_static(self):
+ found = net._klibc_to_config_entry(STATIC_CONTENT_1)
+ self.assertEqual(found, ('eth1', STATIC_EXPECTED_1))
+
+ def test_config_from_klibc_net_cfg(self):
+ files = []
+ pairs = (('net-eth0.cfg', DHCP_CONTENT_1),
+ ('net-eth1.cfg', STATIC_CONTENT_1))
+
+ macs = {'eth1': 'b8:ae:ed:75:ff:2b',
+ 'eth0': 'b8:ae:ed:75:ff:2a'}
+
+ dhcp = copy.deepcopy(DHCP_EXPECTED_1)
+ dhcp['mac_address'] = macs['eth0']
+
+ static = copy.deepcopy(STATIC_EXPECTED_1)
+ static['mac_address'] = macs['eth1']
+
+ expected = {'version': 1, 'config': [dhcp, static]}
+ with util.tempdir() as tmpd:
+ for fname, content in pairs:
+ fp = os.path.join(tmpd, fname)
+ files.append(fp)
+ util.write_file(fp, content)
+
+ found = net.config_from_klibc_net_cfg(files=files, mac_addrs=macs)
+ self.assertEqual(found, expected)
+
+ def test_cmdline_with_b64(self):
+ data = base64.b64encode(json.dumps(self.simple_cfg).encode())
+ encoded_text = data.decode()
+ cmdline = 'ro network-config=' + encoded_text + ' root=foo'
+ found = net.read_kernel_cmdline_config(cmdline=cmdline)
+ self.assertEqual(found, self.simple_cfg)
+
+ def test_cmdline_with_b64_gz(self):
+ data = _gzip_data(json.dumps(self.simple_cfg).encode())
+ encoded_text = base64.b64encode(data).decode()
+ cmdline = 'ro network-config=' + encoded_text + ' root=foo'
+ found = net.read_kernel_cmdline_config(cmdline=cmdline)
+ self.assertEqual(found, self.simple_cfg)
+
+
+def _gzip_data(data):
+ with io.BytesIO() as iobuf:
+ gzfp = gzip.GzipFile(mode="wb", fileobj=iobuf)
+ gzfp.write(data)
+ gzfp.close()
+ return iobuf.getvalue()
diff --git a/tests/unittests/test_pathprefix2dict.py b/tests/unittests/test_pathprefix2dict.py
index 590c4b82..38fd75b6 100644
--- a/tests/unittests/test_pathprefix2dict.py
+++ b/tests/unittests/test_pathprefix2dict.py
@@ -1,37 +1,41 @@
from cloudinit import util
-from mocker import MockerTestCase
-from .helpers import populate_dir
+from .helpers import TestCase, populate_dir
+import shutil
+import tempfile
-class TestPathPrefix2Dict(MockerTestCase):
+
+class TestPathPrefix2Dict(TestCase):
def setUp(self):
- self.tmp = self.makeDir()
+ super(TestPathPrefix2Dict, self).setUp()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def test_required_only(self):
- dirdata = {'f1': 'f1content', 'f2': 'f2content'}
+ dirdata = {'f1': b'f1content', 'f2': b'f2content'}
populate_dir(self.tmp, dirdata)
ret = util.pathprefix2dict(self.tmp, required=['f1', 'f2'])
self.assertEqual(dirdata, ret)
def test_required_missing(self):
- dirdata = {'f1': 'f1content'}
+ dirdata = {'f1': b'f1content'}
populate_dir(self.tmp, dirdata)
kwargs = {'required': ['f1', 'f2']}
self.assertRaises(ValueError, util.pathprefix2dict, self.tmp, **kwargs)
def test_no_required_and_optional(self):
- dirdata = {'f1': 'f1c', 'f2': 'f2c'}
+ dirdata = {'f1': b'f1c', 'f2': b'f2c'}
populate_dir(self.tmp, dirdata)
ret = util.pathprefix2dict(self.tmp, required=None,
- optional=['f1', 'f2'])
+ optional=['f1', 'f2'])
self.assertEqual(dirdata, ret)
def test_required_and_optional(self):
- dirdata = {'f1': 'f1c', 'f2': 'f2c'}
+ dirdata = {'f1': b'f1c', 'f2': b'f2c'}
populate_dir(self.tmp, dirdata)
ret = util.pathprefix2dict(self.tmp, required=['f1'], optional=['f2'])
diff --git a/tests/unittests/test_registry.py b/tests/unittests/test_registry.py
new file mode 100644
index 00000000..bcf01475
--- /dev/null
+++ b/tests/unittests/test_registry.py
@@ -0,0 +1,28 @@
+from cloudinit.registry import DictRegistry
+
+from .helpers import (mock, TestCase)
+
+
+class TestDictRegistry(TestCase):
+
+ def test_added_item_included_in_output(self):
+ registry = DictRegistry()
+ item_key, item_to_register = 'test_key', mock.Mock()
+ registry.register_item(item_key, item_to_register)
+ self.assertEqual({item_key: item_to_register},
+ registry.registered_items)
+
+ def test_registry_starts_out_empty(self):
+ self.assertEqual({}, DictRegistry().registered_items)
+
+ def test_modifying_registered_items_isnt_exposed_to_other_callers(self):
+ registry = DictRegistry()
+ registry.registered_items['test_item'] = mock.Mock()
+ self.assertEqual({}, registry.registered_items)
+
+ def test_keys_cannot_be_replaced(self):
+ registry = DictRegistry()
+ item_key = 'test_key'
+ registry.register_item(item_key, mock.Mock())
+ self.assertRaises(ValueError,
+ registry.register_item, item_key, mock.Mock())
diff --git a/tests/unittests/test_reporting.py b/tests/unittests/test_reporting.py
new file mode 100644
index 00000000..32356ef9
--- /dev/null
+++ b/tests/unittests/test_reporting.py
@@ -0,0 +1,369 @@
+# Copyright 2015 Canonical Ltd.
+# This file is part of cloud-init. See LICENCE file for license information.
+#
+# vi: ts=4 expandtab
+
+from cloudinit import reporting
+from cloudinit.reporting import handlers
+from cloudinit.reporting import events
+
+from .helpers import (mock, TestCase)
+
+
+def _fake_registry():
+ return mock.Mock(registered_items={'a': mock.MagicMock(),
+ 'b': mock.MagicMock()})
+
+
+class TestReportStartEvent(TestCase):
+
+ @mock.patch('cloudinit.reporting.events.instantiated_handler_registry',
+ new_callable=_fake_registry)
+ def test_report_start_event_passes_something_with_as_string_to_handlers(
+ self, instantiated_handler_registry):
+ event_name, event_description = 'my_test_event', 'my description'
+ events.report_start_event(event_name, event_description)
+ expected_string_representation = ': '.join(
+ ['start', event_name, event_description])
+ for _, handler in (
+ instantiated_handler_registry.registered_items.items()):
+ self.assertEqual(1, handler.publish_event.call_count)
+ event = handler.publish_event.call_args[0][0]
+ self.assertEqual(expected_string_representation, event.as_string())
+
+
+class TestReportFinishEvent(TestCase):
+
+ def _report_finish_event(self, result=events.status.SUCCESS):
+ event_name, event_description = 'my_test_event', 'my description'
+ events.report_finish_event(
+ event_name, event_description, result=result)
+ return event_name, event_description
+
+ def assertHandlersPassedObjectWithAsString(
+ self, handlers, expected_as_string):
+ for _, handler in handlers.items():
+ self.assertEqual(1, handler.publish_event.call_count)
+ event = handler.publish_event.call_args[0][0]
+ self.assertEqual(expected_as_string, event.as_string())
+
+ @mock.patch('cloudinit.reporting.events.instantiated_handler_registry',
+ new_callable=_fake_registry)
+ def test_report_finish_event_passes_something_with_as_string_to_handlers(
+ self, instantiated_handler_registry):
+ event_name, event_description = self._report_finish_event()
+ expected_string_representation = ': '.join(
+ ['finish', event_name, events.status.SUCCESS,
+ event_description])
+ self.assertHandlersPassedObjectWithAsString(
+ instantiated_handler_registry.registered_items,
+ expected_string_representation)
+
+ @mock.patch('cloudinit.reporting.events.instantiated_handler_registry',
+ new_callable=_fake_registry)
+ def test_reporting_successful_finish_has_sensible_string_repr(
+ self, instantiated_handler_registry):
+ event_name, event_description = self._report_finish_event(
+ result=events.status.SUCCESS)
+ expected_string_representation = ': '.join(
+ ['finish', event_name, events.status.SUCCESS,
+ event_description])
+ self.assertHandlersPassedObjectWithAsString(
+ instantiated_handler_registry.registered_items,
+ expected_string_representation)
+
+ @mock.patch('cloudinit.reporting.events.instantiated_handler_registry',
+ new_callable=_fake_registry)
+ def test_reporting_unsuccessful_finish_has_sensible_string_repr(
+ self, instantiated_handler_registry):
+ event_name, event_description = self._report_finish_event(
+ result=events.status.FAIL)
+ expected_string_representation = ': '.join(
+ ['finish', event_name, events.status.FAIL, event_description])
+ self.assertHandlersPassedObjectWithAsString(
+ instantiated_handler_registry.registered_items,
+ expected_string_representation)
+
+ def test_invalid_result_raises_attribute_error(self):
+ self.assertRaises(ValueError, self._report_finish_event, ("BOGUS",))
+
+
+class TestReportingEvent(TestCase):
+
+ def test_as_string(self):
+ event_type, name, description = 'test_type', 'test_name', 'test_desc'
+ event = events.ReportingEvent(event_type, name, description)
+ expected_string_representation = ': '.join(
+ [event_type, name, description])
+ self.assertEqual(expected_string_representation, event.as_string())
+
+ def test_as_dict(self):
+ event_type, name, desc = 'test_type', 'test_name', 'test_desc'
+ event = events.ReportingEvent(event_type, name, desc)
+ expected = {'event_type': event_type, 'name': name,
+ 'description': desc, 'origin': 'cloudinit'}
+
+ # allow for timestamp to differ, but must be present
+ as_dict = event.as_dict()
+ self.assertIn('timestamp', as_dict)
+ del as_dict['timestamp']
+
+ self.assertEqual(expected, as_dict)
+
+
+class TestFinishReportingEvent(TestCase):
+ def test_as_has_result(self):
+ result = events.status.SUCCESS
+ name, desc = 'test_name', 'test_desc'
+ event = events.FinishReportingEvent(name, desc, result)
+ ret = event.as_dict()
+ self.assertTrue('result' in ret)
+ self.assertEqual(ret['result'], result)
+
+
+class TestBaseReportingHandler(TestCase):
+
+ def test_base_reporting_handler_is_abstract(self):
+ regexp = r".*abstract.*publish_event.*"
+ self.assertRaisesRegexp(TypeError, regexp, handlers.ReportingHandler)
+
+
+class TestLogHandler(TestCase):
+
+ @mock.patch.object(reporting.handlers.logging, 'getLogger')
+ def test_appropriate_logger_used(self, getLogger):
+ event_type, event_name = 'test_type', 'test_name'
+ event = events.ReportingEvent(event_type, event_name, 'description')
+ reporting.handlers.LogHandler().publish_event(event)
+ self.assertEqual(
+ [mock.call(
+ 'cloudinit.reporting.{0}.{1}'.format(event_type, event_name))],
+ getLogger.call_args_list)
+
+ @mock.patch.object(reporting.handlers.logging, 'getLogger')
+ def test_single_log_message_at_info_published(self, getLogger):
+ event = events.ReportingEvent('type', 'name', 'description')
+ reporting.handlers.LogHandler().publish_event(event)
+ self.assertEqual(1, getLogger.return_value.log.call_count)
+
+ @mock.patch.object(reporting.handlers.logging, 'getLogger')
+ def test_log_message_uses_event_as_string(self, getLogger):
+ event = events.ReportingEvent('type', 'name', 'description')
+ reporting.handlers.LogHandler(level="INFO").publish_event(event)
+ self.assertIn(event.as_string(),
+ getLogger.return_value.log.call_args[0][1])
+
+
+class TestDefaultRegisteredHandler(TestCase):
+
+ def test_log_handler_registered_by_default(self):
+ registered_items = (
+ reporting.instantiated_handler_registry.registered_items)
+ for _, item in registered_items.items():
+ if isinstance(item, reporting.handlers.LogHandler):
+ break
+ else:
+ self.fail('No reporting LogHandler registered by default.')
+
+
+class TestReportingConfiguration(TestCase):
+
+ @mock.patch.object(reporting, 'instantiated_handler_registry')
+ def test_empty_configuration_doesnt_add_handlers(
+ self, instantiated_handler_registry):
+ reporting.update_configuration({})
+ self.assertEqual(
+ 0, instantiated_handler_registry.register_item.call_count)
+
+ @mock.patch.object(
+ reporting, 'instantiated_handler_registry', reporting.DictRegistry())
+ @mock.patch.object(reporting, 'available_handlers')
+ def test_looks_up_handler_by_type_and_adds_it(self, available_handlers):
+ handler_type_name = 'test_handler'
+ handler_cls = mock.Mock()
+ available_handlers.registered_items = {handler_type_name: handler_cls}
+ handler_name = 'my_test_handler'
+ reporting.update_configuration(
+ {handler_name: {'type': handler_type_name}})
+ self.assertEqual(
+ {handler_name: handler_cls.return_value},
+ reporting.instantiated_handler_registry.registered_items)
+
+ @mock.patch.object(
+ reporting, 'instantiated_handler_registry', reporting.DictRegistry())
+ @mock.patch.object(reporting, 'available_handlers')
+ def test_uses_non_type_parts_of_config_dict_as_kwargs(
+ self, available_handlers):
+ handler_type_name = 'test_handler'
+ handler_cls = mock.Mock()
+ available_handlers.registered_items = {handler_type_name: handler_cls}
+ extra_kwargs = {'foo': 'bar', 'bar': 'baz'}
+ handler_config = extra_kwargs.copy()
+ handler_config.update({'type': handler_type_name})
+ handler_name = 'my_test_handler'
+ reporting.update_configuration({handler_name: handler_config})
+ self.assertEqual(
+ handler_cls.return_value,
+ reporting.instantiated_handler_registry.registered_items[
+ handler_name])
+ self.assertEqual([mock.call(**extra_kwargs)],
+ handler_cls.call_args_list)
+
+ @mock.patch.object(
+ reporting, 'instantiated_handler_registry', reporting.DictRegistry())
+ @mock.patch.object(reporting, 'available_handlers')
+ def test_handler_config_not_modified(self, available_handlers):
+ handler_type_name = 'test_handler'
+ handler_cls = mock.Mock()
+ available_handlers.registered_items = {handler_type_name: handler_cls}
+ handler_config = {'type': handler_type_name, 'foo': 'bar'}
+ expected_handler_config = handler_config.copy()
+ reporting.update_configuration({'my_test_handler': handler_config})
+ self.assertEqual(expected_handler_config, handler_config)
+
+ @mock.patch.object(
+ reporting, 'instantiated_handler_registry', reporting.DictRegistry())
+ @mock.patch.object(reporting, 'available_handlers')
+ def test_handlers_removed_if_falseish_specified(self, available_handlers):
+ handler_type_name = 'test_handler'
+ handler_cls = mock.Mock()
+ available_handlers.registered_items = {handler_type_name: handler_cls}
+ handler_name = 'my_test_handler'
+ reporting.update_configuration(
+ {handler_name: {'type': handler_type_name}})
+ self.assertEqual(
+ 1, len(reporting.instantiated_handler_registry.registered_items))
+ reporting.update_configuration({handler_name: None})
+ self.assertEqual(
+ 0, len(reporting.instantiated_handler_registry.registered_items))
+
+
+class TestReportingEventStack(TestCase):
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ @mock.patch('cloudinit.reporting.events.report_start_event')
+ def test_start_and_finish_success(self, report_start, report_finish):
+ with events.ReportEventStack(name="myname", description="mydesc"):
+ pass
+ self.assertEqual(
+ [mock.call('myname', 'mydesc')], report_start.call_args_list)
+ self.assertEqual(
+ [mock.call('myname', 'mydesc', events.status.SUCCESS,
+ post_files=[])],
+ report_finish.call_args_list)
+
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ @mock.patch('cloudinit.reporting.events.report_start_event')
+ def test_finish_exception_defaults_fail(self, report_start, report_finish):
+ name = "myname"
+ desc = "mydesc"
+ try:
+ with events.ReportEventStack(name, description=desc):
+ raise ValueError("This didnt work")
+ except ValueError:
+ pass
+ self.assertEqual([mock.call(name, desc)], report_start.call_args_list)
+ self.assertEqual(
+ [mock.call(name, desc, events.status.FAIL, post_files=[])],
+ report_finish.call_args_list)
+
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ @mock.patch('cloudinit.reporting.events.report_start_event')
+ def test_result_on_exception_used(self, report_start, report_finish):
+ name = "myname"
+ desc = "mydesc"
+ try:
+ with events.ReportEventStack(
+ name, desc, result_on_exception=events.status.WARN):
+ raise ValueError("This didnt work")
+ except ValueError:
+ pass
+ self.assertEqual([mock.call(name, desc)], report_start.call_args_list)
+ self.assertEqual(
+ [mock.call(name, desc, events.status.WARN, post_files=[])],
+ report_finish.call_args_list)
+
+ @mock.patch('cloudinit.reporting.events.report_start_event')
+ def test_child_fullname_respects_parent(self, report_start):
+ parent_name = "topname"
+ c1_name = "c1name"
+ c2_name = "c2name"
+ c2_expected_fullname = '/'.join([parent_name, c1_name, c2_name])
+ c1_expected_fullname = '/'.join([parent_name, c1_name])
+
+ parent = events.ReportEventStack(parent_name, "topdesc")
+ c1 = events.ReportEventStack(c1_name, "c1desc", parent=parent)
+ c2 = events.ReportEventStack(c2_name, "c2desc", parent=c1)
+ with c1:
+ report_start.assert_called_with(c1_expected_fullname, "c1desc")
+ with c2:
+ report_start.assert_called_with(c2_expected_fullname, "c2desc")
+
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ @mock.patch('cloudinit.reporting.events.report_start_event')
+ def test_child_result_bubbles_up(self, report_start, report_finish):
+ parent = events.ReportEventStack("topname", "topdesc")
+ child = events.ReportEventStack("c_name", "c_desc", parent=parent)
+ with parent:
+ with child:
+ child.result = events.status.WARN
+
+ report_finish.assert_called_with(
+ "topname", "topdesc", events.status.WARN, post_files=[])
+
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ def test_message_used_in_finish(self, report_finish):
+ with events.ReportEventStack("myname", "mydesc",
+ message="mymessage"):
+ pass
+ self.assertEqual(
+ [mock.call("myname", "mymessage", events.status.SUCCESS,
+ post_files=[])],
+ report_finish.call_args_list)
+
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ def test_message_updatable(self, report_finish):
+ with events.ReportEventStack("myname", "mydesc") as c:
+ c.message = "all good"
+ self.assertEqual(
+ [mock.call("myname", "all good", events.status.SUCCESS,
+ post_files=[])],
+ report_finish.call_args_list)
+
+ @mock.patch('cloudinit.reporting.events.report_start_event')
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ def test_reporting_disabled_does_not_report_events(
+ self, report_start, report_finish):
+ with events.ReportEventStack("a", "b", reporting_enabled=False):
+ pass
+ self.assertEqual(report_start.call_count, 0)
+ self.assertEqual(report_finish.call_count, 0)
+
+ @mock.patch('cloudinit.reporting.events.report_start_event')
+ @mock.patch('cloudinit.reporting.events.report_finish_event')
+ def test_reporting_child_default_to_parent(
+ self, report_start, report_finish):
+ parent = events.ReportEventStack(
+ "pname", "pdesc", reporting_enabled=False)
+ child = events.ReportEventStack("cname", "cdesc", parent=parent)
+ with parent:
+ with child:
+ pass
+ pass
+ self.assertEqual(report_start.call_count, 0)
+ self.assertEqual(report_finish.call_count, 0)
+
+ def test_reporting_event_has_sane_repr(self):
+ myrep = events.ReportEventStack("fooname", "foodesc",
+ reporting_enabled=True).__repr__()
+ self.assertIn("fooname", myrep)
+ self.assertIn("foodesc", myrep)
+ self.assertIn("True", myrep)
+
+ def test_set_invalid_result_raises_value_error(self):
+ f = events.ReportEventStack("myname", "mydesc")
+ self.assertRaises(ValueError, setattr, f, "result", "BOGUS")
+
+
+class TestStatusAccess(TestCase):
+ def test_invalid_status_access_raises_value_error(self):
+ self.assertRaises(AttributeError, getattr, events.status, "BOGUS")
diff --git a/tests/unittests/test_rh_subscription.py b/tests/unittests/test_rh_subscription.py
new file mode 100644
index 00000000..38d5763a
--- /dev/null
+++ b/tests/unittests/test_rh_subscription.py
@@ -0,0 +1,208 @@
+from cloudinit import util
+from cloudinit.config import cc_rh_subscription
+import logging
+import mock
+import unittest
+
+
+class GoodTests(unittest.TestCase):
+ def setUp(self):
+ super(GoodTests, self).setUp()
+ self.name = "cc_rh_subscription"
+ self.cloud_init = None
+ self.log = logging.getLogger("good_tests")
+ self.args = []
+ self.handle = cc_rh_subscription.handle
+ self.SM = cc_rh_subscription.SubscriptionManager
+
+ self.config = {'rh_subscription':
+ {'username': 'scooby@do.com',
+ 'password': 'scooby-snacks'
+ }}
+ self.config_full = {'rh_subscription':
+ {'username': 'scooby@do.com',
+ 'password': 'scooby-snacks',
+ 'auto-attach': True,
+ 'service-level': 'self-support',
+ 'add-pool': ['pool1', 'pool2', 'pool3'],
+ 'enable-repo': ['repo1', 'repo2', 'repo3'],
+ 'disable-repo': ['repo4', 'repo5']
+ }}
+
+ def test_already_registered(self):
+ '''
+ Emulates a system that is already registered. Ensure it gets
+ a non-ProcessExecution error from is_registered()
+ '''
+ with mock.patch.object(cc_rh_subscription.SubscriptionManager,
+ '_sub_man_cli') as mockobj:
+ self.SM.log_success = mock.MagicMock()
+ self.handle(self.name, self.config, self.cloud_init,
+ self.log, self.args)
+ self.assertEqual(self.SM.log_success.call_count, 1)
+ self.assertEqual(mockobj.call_count, 1)
+
+ def test_simple_registration(self):
+ '''
+ Simple registration with username and password
+ '''
+ self.SM.log_success = mock.MagicMock()
+ reg = "The system has been registered with ID:" \
+ " 12345678-abde-abcde-1234-1234567890abc"
+ self.SM._sub_man_cli = mock.MagicMock(
+ side_effect=[util.ProcessExecutionError, (reg, 'bar')])
+ self.handle(self.name, self.config, self.cloud_init,
+ self.log, self.args)
+ self.assertIn(mock.call(['identity']),
+ self.SM._sub_man_cli.call_args_list)
+ self.assertIn(mock.call(['register', '--username=scooby@do.com',
+ '--password=scooby-snacks'],
+ logstring_val=True),
+ self.SM._sub_man_cli.call_args_list)
+
+ self.assertEqual(self.SM.log_success.call_count, 1)
+ self.assertEqual(self.SM._sub_man_cli.call_count, 2)
+
+ def test_full_registration(self):
+ '''
+ Registration with auto-attach, service-level, adding pools,
+ and enabling and disabling yum repos
+ '''
+ call_lists = []
+ call_lists.append(['attach', '--pool=pool1', '--pool=pool3'])
+ call_lists.append(['repos', '--enable=repo2', '--enable=repo3',
+ '--disable=repo5'])
+ call_lists.append(['attach', '--auto', '--servicelevel=self-support'])
+ self.SM.log_success = mock.MagicMock()
+ reg = "The system has been registered with ID:" \
+ " 12345678-abde-abcde-1234-1234567890abc"
+ self.SM._sub_man_cli = mock.MagicMock(
+ side_effect=[util.ProcessExecutionError, (reg, 'bar'),
+ ('Service level set to: self-support', ''),
+ ('pool1\npool3\n', ''), ('pool2\n', ''), ('', ''),
+ ('Repo ID: repo1\nRepo ID: repo5\n', ''),
+ ('Repo ID: repo2\nRepo ID: repo3\nRepo ID: '
+ 'repo4', ''),
+ ('', '')])
+ self.handle(self.name, self.config_full, self.cloud_init,
+ self.log, self.args)
+ for call in call_lists:
+ self.assertIn(mock.call(call), self.SM._sub_man_cli.call_args_list)
+ self.assertEqual(self.SM.log_success.call_count, 1)
+ self.assertEqual(self.SM._sub_man_cli.call_count, 9)
+
+
+class TestBadInput(unittest.TestCase):
+ name = "cc_rh_subscription"
+ cloud_init = None
+ log = logging.getLogger("bad_tests")
+ args = []
+ SM = cc_rh_subscription.SubscriptionManager
+ reg = "The system has been registered with ID:" \
+ " 12345678-abde-abcde-1234-1234567890abc"
+
+ config_no_password = {'rh_subscription':
+ {'username': 'scooby@do.com'
+ }}
+
+ config_no_key = {'rh_subscription':
+ {'activation-key': '1234abcde',
+ }}
+
+ config_service = {'rh_subscription':
+ {'username': 'scooby@do.com',
+ 'password': 'scooby-snacks',
+ 'service-level': 'self-support'
+ }}
+
+ config_badpool = {'rh_subscription':
+ {'username': 'scooby@do.com',
+ 'password': 'scooby-snacks',
+ 'add-pool': 'not_a_list'
+ }}
+ config_badrepo = {'rh_subscription':
+ {'username': 'scooby@do.com',
+ 'password': 'scooby-snacks',
+ 'enable-repo': 'not_a_list'
+ }}
+ config_badkey = {'rh_subscription':
+ {'activation_key': 'abcdef1234',
+ 'org': '123',
+ }}
+
+ def setUp(self):
+ super(TestBadInput, self).setUp()
+ self.handle = cc_rh_subscription.handle
+
+ def test_no_password(self):
+ '''
+ Attempt to register without the password key/value
+ '''
+ self.input_is_missing_data(self.config_no_password)
+
+ def test_no_org(self):
+ '''
+ Attempt to register without the org key/value
+ '''
+ self.input_is_missing_data(self.config_no_key)
+
+ def test_service_level_without_auto(self):
+ '''
+ Attempt to register using service-level without the auto-attach key
+ '''
+ self.SM.log_warn = mock.MagicMock()
+ self.SM._sub_man_cli = mock.MagicMock(
+ side_effect=[util.ProcessExecutionError, (self.reg, 'bar')])
+ self.handle(self.name, self.config_service, self.cloud_init,
+ self.log, self.args)
+ self.assertEqual(self.SM._sub_man_cli.call_count, 1)
+ self.assertEqual(self.SM.log_warn.call_count, 2)
+
+ def test_pool_not_a_list(self):
+ '''
+ Register with pools that are not in the format of a list
+ '''
+ self.SM.log_warn = mock.MagicMock()
+ self.SM._sub_man_cli = mock.MagicMock(
+ side_effect=[util.ProcessExecutionError, (self.reg, 'bar')])
+ self.handle(self.name, self.config_badpool, self.cloud_init,
+ self.log, self.args)
+ self.assertEqual(self.SM._sub_man_cli.call_count, 2)
+ self.assertEqual(self.SM.log_warn.call_count, 2)
+
+ def test_repo_not_a_list(self):
+ '''
+ Register with repos that are not in the format of a list
+ '''
+ self.SM.log_warn = mock.MagicMock()
+ self.SM._sub_man_cli = mock.MagicMock(
+ side_effect=[util.ProcessExecutionError, (self.reg, 'bar')])
+ self.handle(self.name, self.config_badrepo, self.cloud_init,
+ self.log, self.args)
+ self.assertEqual(self.SM.log_warn.call_count, 3)
+ self.assertEqual(self.SM._sub_man_cli.call_count, 2)
+
+ def test_bad_key_value(self):
+ '''
+ Attempt to register with a key that we don't know
+ '''
+ self.SM.log_warn = mock.MagicMock()
+ self.SM._sub_man_cli = mock.MagicMock(
+ side_effect=[util.ProcessExecutionError, (self.reg, 'bar')])
+ self.handle(self.name, self.config_badkey, self.cloud_init,
+ self.log, self.args)
+ self.assertEqual(self.SM.log_warn.call_count, 2)
+ self.assertEqual(self.SM._sub_man_cli.call_count, 1)
+
+ def input_is_missing_data(self, config):
+ '''
+ Helper def for tests that having missing information
+ '''
+ self.SM.log_warn = mock.MagicMock()
+ self.SM._sub_man_cli = mock.MagicMock(
+ side_effect=[util.ProcessExecutionError])
+ self.handle(self.name, config, self.cloud_init,
+ self.log, self.args)
+ self.SM._sub_man_cli.assert_called_with(['identity'])
+ self.assertEqual(self.SM.log_warn.call_count, 4)
+ self.assertEqual(self.SM._sub_man_cli.call_count, 1)
diff --git a/tests/unittests/test_runs/test_merge_run.py b/tests/unittests/test_runs/test_merge_run.py
index 977adb34..d0ec36a9 100644
--- a/tests/unittests/test_runs/test_merge_run.py
+++ b/tests/unittests/test_runs/test_merge_run.py
@@ -1,20 +1,22 @@
import os
+import shutil
+import tempfile
from .. import helpers
-from cloudinit.settings import (PER_INSTANCE)
+from cloudinit.settings import PER_INSTANCE
from cloudinit import stages
from cloudinit import util
class TestMergeRun(helpers.FilesystemMockingTestCase):
def _patchIn(self, root):
- self.restore()
self.patchOS(root)
self.patchUtils(root)
def test_none_ds(self):
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self.replicateTestRoot('simple_ubuntu', new_root)
cfg = {
'datasource_list': ['None'],
diff --git a/tests/unittests/test_runs/test_simple_run.py b/tests/unittests/test_runs/test_simple_run.py
index c9ba949e..e19e65cd 100644
--- a/tests/unittests/test_runs/test_simple_run.py
+++ b/tests/unittests/test_runs/test_simple_run.py
@@ -1,20 +1,20 @@
import os
+import shutil
+import tempfile
from .. import helpers
-from cloudinit.settings import (PER_INSTANCE)
+from cloudinit.settings import PER_INSTANCE
from cloudinit import stages
from cloudinit import util
class TestSimpleRun(helpers.FilesystemMockingTestCase):
def _patchIn(self, root):
- self.restore()
self.patchOS(root)
self.patchUtils(root)
def _pp_root(self, root, repatch=True):
- self.restore()
for (dirpath, dirnames, filenames) in os.walk(root):
print(dirpath)
for f in filenames:
@@ -33,7 +33,8 @@ class TestSimpleRun(helpers.FilesystemMockingTestCase):
self._patchIn(root)
def test_none_ds(self):
- new_root = self.makeDir()
+ new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, new_root)
self.replicateTestRoot('simple_ubuntu', new_root)
cfg = {
'datasource_list': ['None'],
@@ -41,7 +42,7 @@ class TestSimpleRun(helpers.FilesystemMockingTestCase):
{
'path': '/etc/blah.ini',
'content': 'blah',
- 'permissions': 0755,
+ 'permissions': 0o755,
},
],
'cloud_init_modules': ['write-files'],
diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py
index 3b317121..9aeb1cde 100644
--- a/tests/unittests/test_sshutil.py
+++ b/tests/unittests/test_sshutil.py
@@ -32,7 +32,8 @@ VALID_CONTENT = {
),
}
-TEST_OPTIONS = ("no-port-forwarding,no-agent-forwarding,no-X11-forwarding,"
+TEST_OPTIONS = (
+ "no-port-forwarding,no-agent-forwarding,no-X11-forwarding,"
'command="echo \'Please login as the user \"ubuntu\" rather than the'
'user \"root\".\';echo;sleep 10"')
diff --git a/tests/unittests/test_templating.py b/tests/unittests/test_templating.py
index 3ba4ed8a..b9863650 100644
--- a/tests/unittests/test_templating.py
+++ b/tests/unittests/test_templating.py
@@ -16,11 +16,20 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from __future__ import print_function
+
from . import helpers as test_helpers
import textwrap
from cloudinit import templater
+try:
+ import Cheetah
+ HAS_CHEETAH = True
+ Cheetah # make pyflakes happy, as Cheetah is not used here
+except ImportError:
+ HAS_CHEETAH = False
+
class TestTemplates(test_helpers.TestCase):
def test_render_basic(self):
@@ -38,6 +47,7 @@ class TestTemplates(test_helpers.TestCase):
out_data = templater.basic_render(in_data, {'b': 2})
self.assertEqual(expected_data.strip(), out_data)
+ @test_helpers.skipIf(not HAS_CHEETAH, 'cheetah renderer not available')
def test_detection(self):
blob = "## template:cheetah"
@@ -104,5 +114,6 @@ $a,$b'''
codename)
out_data = templater.basic_render(in_data,
- {'mirror': mirror, 'codename': codename})
+ {'mirror': mirror,
+ 'codename': codename})
self.assertEqual(ex_data, out_data)
diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
index 35e92445..37a984ac 100644
--- a/tests/unittests/test_util.py
+++ b/tests/unittests/test_util.py
@@ -1,13 +1,21 @@
+from __future__ import print_function
+
+import logging
import os
+import shutil
import stat
+import tempfile
+
+import six
import yaml
-from mocker import MockerTestCase
+from cloudinit import importer, util
from . import helpers
-import unittest
-from cloudinit import importer
-from cloudinit import util
+try:
+ from unittest import mock
+except ImportError:
+ import mock
class FakeSelinux(object):
@@ -29,7 +37,7 @@ class FakeSelinux(object):
self.restored.append(path)
-class TestGetCfgOptionListOrStr(unittest.TestCase):
+class TestGetCfgOptionListOrStr(helpers.TestCase):
def test_not_found_no_default(self):
"""None is returned if key is not found and no default given."""
config = {}
@@ -61,10 +69,11 @@ class TestGetCfgOptionListOrStr(unittest.TestCase):
self.assertEqual([], result)
-class TestWriteFile(MockerTestCase):
+class TestWriteFile(helpers.TestCase):
def setUp(self):
super(TestWriteFile, self).setUp()
- self.tmp = self.makeDir(prefix="unittest_")
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def test_basic_usage(self):
"""Verify basic usage with default args."""
@@ -79,7 +88,7 @@ class TestWriteFile(MockerTestCase):
create_contents = f.read()
self.assertEqual(contents, create_contents)
file_stat = os.stat(path)
- self.assertEqual(0644, stat.S_IMODE(file_stat.st_mode))
+ self.assertEqual(0o644, stat.S_IMODE(file_stat.st_mode))
def test_dir_is_created_if_required(self):
"""Verifiy that directories are created is required."""
@@ -97,12 +106,12 @@ class TestWriteFile(MockerTestCase):
path = os.path.join(self.tmp, "NewFile.txt")
contents = "Hey there"
- util.write_file(path, contents, mode=0666)
+ util.write_file(path, contents, mode=0o666)
self.assertTrue(os.path.exists(path))
self.assertTrue(os.path.isfile(path))
file_stat = os.stat(path)
- self.assertEqual(0666, stat.S_IMODE(file_stat.st_mode))
+ self.assertEqual(0o666, stat.S_IMODE(file_stat.st_mode))
def test_custom_omode(self):
"""Verify custom omode works properly."""
@@ -111,7 +120,7 @@ class TestWriteFile(MockerTestCase):
# Create file first with basic content
with open(path, "wb") as f:
- f.write("LINE1\n")
+ f.write(b"LINE1\n")
util.write_file(path, contents, omode="a")
self.assertTrue(os.path.exists(path))
@@ -126,23 +135,24 @@ class TestWriteFile(MockerTestCase):
with open(my_file, "w") as fp:
fp.write("My Content")
- import_mock = self.mocker.replace(importer.import_module,
- passthrough=False)
- import_mock('selinux')
-
fake_se = FakeSelinux(my_file)
- self.mocker.result(fake_se)
- self.mocker.replay()
- with util.SeLinuxGuard(my_file) as is_on:
- self.assertTrue(is_on)
+
+ with mock.patch.object(importer, 'import_module',
+ return_value=fake_se) as mockobj:
+ with util.SeLinuxGuard(my_file) as is_on:
+ self.assertTrue(is_on)
+
self.assertEqual(1, len(fake_se.restored))
self.assertEqual(my_file, fake_se.restored[0])
+ mockobj.assert_called_once_with('selinux')
+
-class TestDeleteDirContents(MockerTestCase):
+class TestDeleteDirContents(helpers.TestCase):
def setUp(self):
super(TestDeleteDirContents, self).setUp()
- self.tmp = self.makeDir(prefix="unittest_")
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
def assertDirEmpty(self, dirname):
self.assertEqual([], os.listdir(dirname))
@@ -157,7 +167,7 @@ class TestDeleteDirContents(MockerTestCase):
def test_deletes_files(self):
"""Single file should be deleted."""
with open(os.path.join(self.tmp, "new_file.txt"), "wb") as f:
- f.write("DELETE ME")
+ f.write(b"DELETE ME")
util.delete_dir_contents(self.tmp)
@@ -185,7 +195,7 @@ class TestDeleteDirContents(MockerTestCase):
os.mkdir(os.path.join(self.tmp, "new_dir"))
f_name = os.path.join(self.tmp, "new_dir", "new_file.txt")
with open(f_name, "wb") as f:
- f.write("DELETE ME")
+ f.write(b"DELETE ME")
util.delete_dir_contents(self.tmp)
@@ -196,7 +206,7 @@ class TestDeleteDirContents(MockerTestCase):
file_name = os.path.join(self.tmp, "new_file.txt")
link_name = os.path.join(self.tmp, "new_file_link.txt")
with open(file_name, "wb") as f:
- f.write("DELETE ME")
+ f.write(b"DELETE ME")
os.symlink(file_name, link_name)
util.delete_dir_contents(self.tmp)
@@ -204,20 +214,20 @@ class TestDeleteDirContents(MockerTestCase):
self.assertDirEmpty(self.tmp)
-class TestKeyValStrings(unittest.TestCase):
+class TestKeyValStrings(helpers.TestCase):
def test_keyval_str_to_dict(self):
expected = {'1': 'one', '2': 'one+one', 'ro': True}
cmdline = "1=one ro 2=one+one"
self.assertEqual(expected, util.keyval_str_to_dict(cmdline))
-class TestGetCmdline(unittest.TestCase):
+class TestGetCmdline(helpers.TestCase):
def test_cmdline_reads_debug_env(self):
os.environ['DEBUG_PROC_CMDLINE'] = 'abcd 123'
self.assertEqual(os.environ['DEBUG_PROC_CMDLINE'], util.get_cmdline())
-class TestLoadYaml(unittest.TestCase):
+class TestLoadYaml(helpers.TestCase):
mydefault = "7b03a8ebace993d806255121073fed52"
def test_simple(self):
@@ -246,8 +256,8 @@ class TestLoadYaml(unittest.TestCase):
self.mydefault)
def test_python_unicode(self):
- # complex type of python/unicde is explicitly allowed
- myobj = {'1': unicode("FOOBAR")}
+ # complex type of python/unicode is explicitly allowed
+ myobj = {'1': six.text_type("FOOBAR")}
safe_yaml = yaml.dump(myobj)
self.assertEqual(util.load_yaml(blob=safe_yaml,
default=self.mydefault),
@@ -310,4 +320,170 @@ class TestMountinfoParsing(helpers.ResourceUsingTestCase):
expected = ('none', 'tmpfs', '/run/lock')
self.assertEqual(expected, util.parse_mount_info('/run/lock', lines))
+
+class TestReadDMIData(helpers.FilesystemMockingTestCase):
+
+ def setUp(self):
+ super(TestReadDMIData, self).setUp()
+ self.new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.new_root)
+ self.patchOS(self.new_root)
+ self.patchUtils(self.new_root)
+
+ def _create_sysfs_parent_directory(self):
+ util.ensure_dir(os.path.join('sys', 'class', 'dmi', 'id'))
+
+ def _create_sysfs_file(self, key, content):
+ """Mocks the sys path found on Linux systems."""
+ self._create_sysfs_parent_directory()
+ dmi_key = "/sys/class/dmi/id/{0}".format(key)
+ util.write_file(dmi_key, content)
+
+ def _configure_dmidecode_return(self, key, content, error=None):
+ """
+ In order to test a missing sys path and call outs to dmidecode, this
+ function fakes the results of dmidecode to test the results.
+ """
+ def _dmidecode_subp(cmd):
+ if cmd[-1] != key:
+ raise util.ProcessExecutionError()
+ return (content, error)
+
+ self.patched_funcs.enter_context(
+ mock.patch.object(util, 'which', lambda _: True))
+ self.patched_funcs.enter_context(
+ mock.patch.object(util, 'subp', _dmidecode_subp))
+
+ def patch_mapping(self, new_mapping):
+ self.patched_funcs.enter_context(
+ mock.patch('cloudinit.util.DMIDECODE_TO_DMI_SYS_MAPPING',
+ new_mapping))
+
+ def test_sysfs_used_with_key_in_mapping_and_file_on_disk(self):
+ self.patch_mapping({'mapped-key': 'mapped-value'})
+ expected_dmi_value = 'sys-used-correctly'
+ self._create_sysfs_file('mapped-value', expected_dmi_value)
+ self._configure_dmidecode_return('mapped-key', 'wrong-wrong-wrong')
+ self.assertEqual(expected_dmi_value, util.read_dmi_data('mapped-key'))
+
+ def test_dmidecode_used_if_no_sysfs_file_on_disk(self):
+ self.patch_mapping({})
+ self._create_sysfs_parent_directory()
+ expected_dmi_value = 'dmidecode-used'
+ self._configure_dmidecode_return('use-dmidecode', expected_dmi_value)
+ self.assertEqual(expected_dmi_value,
+ util.read_dmi_data('use-dmidecode'))
+
+ def test_none_returned_if_neither_source_has_data(self):
+ self.patch_mapping({})
+ self._configure_dmidecode_return('key', 'value')
+ self.assertEqual(None, util.read_dmi_data('expect-fail'))
+
+ def test_none_returned_if_dmidecode_not_in_path(self):
+ self.patched_funcs.enter_context(
+ mock.patch.object(util, 'which', lambda _: False))
+ self.patch_mapping({})
+ self.assertEqual(None, util.read_dmi_data('expect-fail'))
+
+ def test_dots_returned_instead_of_foxfox(self):
+ # uninitialized dmi values show as \xff, return those as .
+ my_len = 32
+ dmi_value = b'\xff' * my_len + b'\n'
+ expected = ""
+ dmi_key = 'system-product-name'
+ sysfs_key = 'product_name'
+ self._create_sysfs_file(sysfs_key, dmi_value)
+ self.assertEqual(expected, util.read_dmi_data(dmi_key))
+
+
+class TestMultiLog(helpers.FilesystemMockingTestCase):
+
+ def _createConsole(self, root):
+ os.mkdir(os.path.join(root, 'dev'))
+ open(os.path.join(root, 'dev', 'console'), 'a').close()
+
+ def setUp(self):
+ super(TestMultiLog, self).setUp()
+ self.root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.root)
+ self.patchOS(self.root)
+ self.patchUtils(self.root)
+ self.patchOpen(self.root)
+ self.stdout = six.StringIO()
+ self.stderr = six.StringIO()
+ self.patchStdoutAndStderr(self.stdout, self.stderr)
+
+ def test_stderr_used_by_default(self):
+ logged_string = 'test stderr output'
+ util.multi_log(logged_string)
+ self.assertEqual(logged_string, self.stderr.getvalue())
+
+ def test_stderr_not_used_if_false(self):
+ util.multi_log('should not see this', stderr=False)
+ self.assertEqual('', self.stderr.getvalue())
+
+ def test_logs_go_to_console_by_default(self):
+ self._createConsole(self.root)
+ logged_string = 'something very important'
+ util.multi_log(logged_string)
+ self.assertEqual(logged_string, open('/dev/console').read())
+
+ def test_logs_dont_go_to_stdout_if_console_exists(self):
+ self._createConsole(self.root)
+ util.multi_log('something')
+ self.assertEqual('', self.stdout.getvalue())
+
+ def test_logs_go_to_stdout_if_console_does_not_exist(self):
+ logged_string = 'something very important'
+ util.multi_log(logged_string)
+ self.assertEqual(logged_string, self.stdout.getvalue())
+
+ def test_logs_go_to_log_if_given(self):
+ log = mock.MagicMock()
+ logged_string = 'something very important'
+ util.multi_log(logged_string, log=log)
+ self.assertEqual([((mock.ANY, logged_string), {})],
+ log.log.call_args_list)
+
+ def test_newlines_stripped_from_log_call(self):
+ log = mock.MagicMock()
+ expected_string = 'something very important'
+ util.multi_log('{0}\n'.format(expected_string), log=log)
+ self.assertEqual((mock.ANY, expected_string), log.log.call_args[0])
+
+ def test_log_level_defaults_to_debug(self):
+ log = mock.MagicMock()
+ util.multi_log('message', log=log)
+ self.assertEqual((logging.DEBUG, mock.ANY), log.log.call_args[0])
+
+ def test_given_log_level_used(self):
+ log = mock.MagicMock()
+ log_level = mock.Mock()
+ util.multi_log('message', log=log, log_level=log_level)
+ self.assertEqual((log_level, mock.ANY), log.log.call_args[0])
+
+
+class TestMessageFromString(helpers.TestCase):
+
+ def test_unicode_not_messed_up(self):
+ roundtripped = util.message_from_string(u'\n').as_string()
+ self.assertNotIn('\x00', roundtripped)
+
+
+class TestReadSeeded(helpers.TestCase):
+ def setUp(self):
+ super(TestReadSeeded, self).setUp()
+ self.tmp = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmp)
+
+ def test_unicode_not_messed_up(self):
+ ud = b"userdatablob"
+ helpers.populate_dir(
+ self.tmp, {'meta-data': "key1: val1", 'user-data': ud})
+ sdir = self.tmp + os.path.sep
+ (found_md, found_ud) = util.read_seeded(sdir)
+
+ self.assertEqual(found_md, {'key1': 'val1'})
+ self.assertEqual(found_ud, ud)
+
# vi: ts=4 expandtab
diff --git a/tests/unittests/test_vmware_config_file.py b/tests/unittests/test_vmware_config_file.py
new file mode 100644
index 00000000..d5c7367b
--- /dev/null
+++ b/tests/unittests/test_vmware_config_file.py
@@ -0,0 +1,103 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2015 Canonical Ltd.
+# Copyright (C) 2016 VMware INC.
+#
+# Author: Sankar Tanguturi <stanguturi@vmware.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+import sys
+import unittest
+
+from cloudinit.sources.helpers.vmware.imc.boot_proto import BootProtoEnum
+from cloudinit.sources.helpers.vmware.imc.config import Config
+from cloudinit.sources.helpers.vmware.imc.config_file import ConfigFile
+
+logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
+logger = logging.getLogger(__name__)
+
+
+class TestVmwareConfigFile(unittest.TestCase):
+
+ def test_utility_methods(self):
+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
+
+ cf.clear()
+
+ self.assertEqual(0, len(cf), "clear size")
+
+ cf._insertKey(" PASSWORD|-PASS ", " foo ")
+ cf._insertKey("BAR", " ")
+
+ self.assertEqual(2, len(cf), "insert size")
+ self.assertEqual('foo', cf["PASSWORD|-PASS"], "password")
+ self.assertTrue("PASSWORD|-PASS" in cf, "hasPassword")
+ self.assertFalse(cf.should_keep_current_value("PASSWORD|-PASS"),
+ "keepPassword")
+ self.assertFalse(cf.should_remove_current_value("PASSWORD|-PASS"),
+ "removePassword")
+ self.assertFalse("FOO" in cf, "hasFoo")
+ self.assertTrue(cf.should_keep_current_value("FOO"), "keepFoo")
+ self.assertFalse(cf.should_remove_current_value("FOO"), "removeFoo")
+ self.assertTrue("BAR" in cf, "hasBar")
+ self.assertFalse(cf.should_keep_current_value("BAR"), "keepBar")
+ self.assertTrue(cf.should_remove_current_value("BAR"), "removeBar")
+
+ def test_configfile_static_2nics(self):
+ cf = ConfigFile("tests/data/vmware/cust-static-2nic.cfg")
+
+ conf = Config(cf)
+
+ self.assertEqual('myhost1', conf.host_name, "hostName")
+ self.assertEqual('Africa/Abidjan', conf.timezone, "tz")
+ self.assertTrue(conf.utc, "utc")
+
+ self.assertEqual(['10.20.145.1', '10.20.145.2'],
+ conf.name_servers,
+ "dns")
+ self.assertEqual(['eng.vmware.com', 'proxy.vmware.com'],
+ conf.dns_suffixes,
+ "suffixes")
+
+ nics = conf.nics
+ ipv40 = nics[0].staticIpv4
+
+ self.assertEqual(2, len(nics), "nics")
+ self.assertEqual('NIC1', nics[0].name, "nic0")
+ self.assertEqual('00:50:56:a6:8c:08', nics[0].mac, "mac0")
+ self.assertEqual(BootProtoEnum.STATIC, nics[0].bootProto, "bootproto0")
+ self.assertEqual('10.20.87.154', ipv40[0].ip, "ipv4Addr0")
+ self.assertEqual('255.255.252.0', ipv40[0].netmask, "ipv4Mask0")
+ self.assertEqual(2, len(ipv40[0].gateways), "ipv4Gw0")
+ self.assertEqual('10.20.87.253', ipv40[0].gateways[0], "ipv4Gw0_0")
+ self.assertEqual('10.20.87.105', ipv40[0].gateways[1], "ipv4Gw0_1")
+
+ self.assertEqual(1, len(nics[0].staticIpv6), "ipv6Cnt0")
+ self.assertEqual('fc00:10:20:87::154',
+ nics[0].staticIpv6[0].ip,
+ "ipv6Addr0")
+
+ self.assertEqual('NIC2', nics[1].name, "nic1")
+ self.assertTrue(not nics[1].staticIpv6, "ipv61 dhcp")
+
+ def test_config_file_dhcp_2nics(self):
+ cf = ConfigFile("tests/data/vmware/cust-dhcp-2nic.cfg")
+
+ conf = Config(cf)
+ nics = conf.nics
+ self.assertEqual(2, len(nics), "nics")
+ self.assertEqual('NIC1', nics[0].name, "nic0")
+ self.assertEqual('00:50:56:a6:8c:08', nics[0].mac, "mac0")
+ self.assertEqual(BootProtoEnum.DHCP, nics[0].bootProto, "bootproto0")
diff --git a/tools/ccfg-merge-debug b/tools/ccfg-merge-debug
index 85227da7..1f08e0cb 100755
--- a/tools/ccfg-merge-debug
+++ b/tools/ccfg-merge-debug
@@ -51,7 +51,7 @@ def main():
c_handlers.register(ccph)
called = []
- for (_ctype, mod) in c_handlers.iteritems():
+ for (_ctype, mod) in c_handlers.items():
if mod in called:
continue
handlers.call_begin(mod, data, frequency)
@@ -76,7 +76,7 @@ def main():
# Give callbacks opportunity to finalize
called = []
- for (_ctype, mod) in c_handlers.iteritems():
+ for (_ctype, mod) in c_handlers.items():
if mod in called:
continue
handlers.call_end(mod, data, frequency)
diff --git a/tools/hacking.py b/tools/hacking.py
index e7797564..716c1154 100755
--- a/tools/hacking.py
+++ b/tools/hacking.py
@@ -47,10 +47,10 @@ def import_normalize(line):
# handle "from x import y as z" to "import x.y as z"
split_line = line.split()
if (line.startswith("from ") and "," not in line and
- split_line[2] == "import" and split_line[3] != "*" and
- split_line[1] != "__future__" and
- (len(split_line) == 4 or
- (len(split_line) == 6 and split_line[4] == "as"))):
+ split_line[2] == "import" and split_line[3] != "*" and
+ split_line[1] != "__future__" and
+ (len(split_line) == 4 or (len(split_line) == 6 and
+ split_line[4] == "as"))):
return "import %s.%s" % (split_line[1], split_line[3])
else:
return line
@@ -74,7 +74,7 @@ def cloud_import_alphabetical(physical_line, line_number, lines):
split_line[0] == "import" and split_previous[0] == "import"):
if split_line[1] < split_previous[1]:
return (0, "N306: imports not in alphabetical order (%s, %s)"
- % (split_previous[1], split_line[1]))
+ % (split_previous[1], split_line[1]))
def cloud_docstring_start_space(physical_line):
@@ -87,8 +87,8 @@ def cloud_docstring_start_space(physical_line):
pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE]) # start
if (pos != -1 and len(physical_line) > pos + 1):
if (physical_line[pos + 3] == ' '):
- return (pos, "N401: one line docstring should not start with"
- " a space")
+ return (pos,
+ "N401: one line docstring should not start with a space")
def cloud_todo_format(physical_line):
@@ -128,7 +128,7 @@ def cloud_docstring_multiline_end(physical_line):
"""
pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE]) # start
if (pos != -1 and len(physical_line) == pos):
- print physical_line
+ print(physical_line)
if (physical_line[pos + 3] == ' '):
return (pos, "N403: multi line docstring end on new line")
@@ -167,4 +167,4 @@ if __name__ == "__main__":
finally:
if len(_missingImport) > 0:
print >> sys.stderr, ("%i imports missing in this test environment"
- % len(_missingImport))
+ % len(_missingImport))
diff --git a/tools/mock-meta.py b/tools/mock-meta.py
index dfbc2a71..1c746f17 100755
--- a/tools/mock-meta.py
+++ b/tools/mock-meta.py
@@ -126,11 +126,11 @@ class WebException(Exception):
def yamlify(data):
formatted = yaml.dump(data,
- line_break="\n",
- indent=4,
- explicit_start=True,
- explicit_end=True,
- default_flow_style=False)
+ line_break="\n",
+ indent=4,
+ explicit_start=True,
+ explicit_end=True,
+ default_flow_style=False)
return formatted
@@ -282,7 +282,7 @@ class MetaDataHandler(object):
else:
log.warn(("Did not implement action %s, "
"returning empty response: %r"),
- action, NOT_IMPL_RESPONSE)
+ action, NOT_IMPL_RESPONSE)
return NOT_IMPL_RESPONSE
@@ -404,14 +404,17 @@ def setup_logging(log_level, fmt='%(levelname)s: @%(name)s : %(message)s'):
def extract_opts():
parser = OptionParser()
parser.add_option("-p", "--port", dest="port", action="store", type=int,
- default=80, metavar="PORT",
- help="port from which to serve traffic (default: %default)")
+ default=80, metavar="PORT",
+ help=("port from which to serve traffic"
+ " (default: %default)"))
parser.add_option("-a", "--addr", dest="address", action="store", type=str,
- default='0.0.0.0', metavar="ADDRESS",
- help="address from which to serve traffic (default: %default)")
+ default='0.0.0.0', metavar="ADDRESS",
+ help=("address from which to serve traffic"
+ " (default: %default)"))
parser.add_option("-f", '--user-data-file', dest='user_data_file',
- action='store', metavar='FILE',
- help="user data filename to serve back to incoming requests")
+ action='store', metavar='FILE',
+ help=("user data filename to serve back to"
+ "incoming requests"))
(options, args) = parser.parse_args()
out = dict()
out['extra'] = args
diff --git a/tools/read-dependencies b/tools/read-dependencies
index fee3efcf..6a6f3e12 100755
--- a/tools/read-dependencies
+++ b/tools/read-dependencies
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import os
+import re
import sys
if 'CLOUD_INIT_TOP_D' in os.environ:
@@ -14,10 +15,15 @@ for fname in ("setup.py", "requirements.txt"):
"exist in cloud-init root directory." % fname)
sys.exit(1)
-with open(os.path.join(topd, "requirements.txt"), "r") as fp:
+if len(sys.argv) > 1:
+ reqfile = sys.argv[1]
+else:
+ reqfile = "requirements.txt"
+
+with open(os.path.join(topd, reqfile), "r") as fp:
for line in fp:
if not line.strip() or line.startswith("#"):
continue
- sys.stdout.write(line)
+ sys.stdout.write(re.split("[>=.<]*", line)[0].strip() + "\n")
sys.exit(0)
diff --git a/tools/run-pep8 b/tools/run-pep8
index ccd6be5a..086400fc 100755
--- a/tools/run-pep8
+++ b/tools/run-pep8
@@ -1,39 +1,22 @@
#!/bin/bash
-if [ $# -eq 0 ]; then
- files=( bin/cloud-init $(find * -name "*.py" -type f) )
+pycheck_dirs=( "cloudinit/" "bin/" "tests/" "tools/" )
+# FIXME: cloud-init modifies sys module path, pep8 does not like
+# bin_files=( "bin/cloud-init" )
+CR="
+"
+[ "$1" = "-v" ] && { verbose="$1"; shift; } || verbose=""
+
+set -f
+if [ $# -eq 0 ]; then unset IFS
+ IFS="$CR"
+ files=( "${bin_files[@]}" "${pycheck_dirs[@]}" )
+ unset IFS
else
- files=( "$@" );
+ files=( "$@" )
fi
-if [ -f 'hacking.py' ]
-then
- base=`pwd`
-else
- base=`pwd`/tools/
-fi
-
-IGNORE=""
-
-# King Arthur: Be quiet! ... Be Quiet! I Order You to Be Quiet.
-IGNORE="$IGNORE,E121" # Continuation line indentation is not a multiple of four
-IGNORE="$IGNORE,E123" # Closing bracket does not match indentation of opening bracket's line
-IGNORE="$IGNORE,E124" # Closing bracket missing visual indentation
-IGNORE="$IGNORE,E125" # Continuation line does not distinguish itself from next logical line
-IGNORE="$IGNORE,E126" # Continuation line over-indented for hanging indent
-IGNORE="$IGNORE,E127" # Continuation line over-indented for visual indent
-IGNORE="$IGNORE,E128" # Continuation line under-indented for visual indent
-IGNORE="$IGNORE,E502" # The backslash is redundant between brackets
-IGNORE="${IGNORE#,}" # remove the leading ',' added above
-
-cmd=(
- ${base}/hacking.py
-
- --ignore="$IGNORE"
-
- "${files[@]}"
-)
-
-echo -e "\nRunning 'cloudinit' pep8:"
-echo "${cmd[@]}"
-"${cmd[@]}"
+myname=${0##*/}
+cmd=( "${myname#run-}" $verbose "${files[@]}" )
+echo "Running: " "${cmd[@]}" 1>&2
+exec "${cmd[@]}"
diff --git a/tools/run-pyflakes b/tools/run-pyflakes
new file mode 100755
index 00000000..4bea17f4
--- /dev/null
+++ b/tools/run-pyflakes
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+PYTHON_VERSION=${PYTHON_VERSION:-2}
+CR="
+"
+pycheck_dirs=( "cloudinit/" "bin/" "tests/" "tools/" )
+
+set -f
+if [ $# -eq 0 ]; then
+ files=( "${pycheck_dirs[@]}" )
+else
+ files=( "$@" )
+fi
+
+cmd=( "python${PYTHON_VERSION}" -m "pyflakes" "${files[@]}" )
+
+echo "Running: " "${cmd[@]}" 1>&2
+exec "${cmd[@]}"
diff --git a/tools/run-pyflakes3 b/tools/run-pyflakes3
new file mode 100755
index 00000000..e9f0863d
--- /dev/null
+++ b/tools/run-pyflakes3
@@ -0,0 +1,2 @@
+#!/bin/sh
+PYTHON_VERSION=3 exec "${0%/*}/run-pyflakes" "$@"
diff --git a/tools/tox-venv b/tools/tox-venv
new file mode 100755
index 00000000..76ed5076
--- /dev/null
+++ b/tools/tox-venv
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+error() { echo "$@" 1>&2; }
+fail() { [ $# -eq 0 ] || error "$@"; exit 1; }
+Usage() {
+ cat <<EOF
+Usage: ${0##*/} tox-environment [command [args]]
+ run command with provided arguments in the provided tox environment
+ command defaults to \${SHELL:-/bin/sh}.
+
+ invoke with '--list' to show available environments
+EOF
+}
+list_toxes() {
+ local td="$1" pre="$2" d=""
+ ( cd "$tox_d" &&
+ for d in *; do [ -f "$d/bin/activate" ] && echo "${pre}$d"; done)
+}
+
+[ $# -eq 0 ] && { Usage 1>&2; exit 1; }
+[ "$1" = "-h" -o "$1" = "--help" ] && { Usage; exit 0; }
+
+env="$1"
+shift
+tox_d="${0%/*}/../.tox"
+activate="$tox_d/$env/bin/activate"
+
+
+[ -d "$tox_d" ] || fail "$tox_d: not a dir. maybe run 'tox'?"
+
+[ "$env" = "-l" -o "$env" = "--list" ] && { list_toxes ; exit ; }
+
+if [ ! -f "$activate" ]; then
+ error "$env: not a valid tox environment?"
+ error "try one of:"
+ list_toxes "$tox_d" " "
+ fail
+fi
+. "$activate"
+
+[ "$#" -gt 0 ] || set -- ${SHELL:-/bin/bash}
+debian_chroot="tox:$env" exec "$@"
diff --git a/tools/validate-yaml.py b/tools/validate-yaml.py
index eda59cb8..ed9037d9 100755
--- a/tools/validate-yaml.py
+++ b/tools/validate-yaml.py
@@ -1,10 +1,9 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
"""Try to read a YAML file and report any errors.
"""
import sys
-
import yaml
@@ -17,7 +16,7 @@ if __name__ == "__main__":
yaml.safe_load(fh.read())
fh.close()
sys.stdout.write(" - ok\n")
- except Exception, e:
+ except Exception as e:
sys.stdout.write(" - bad (%s)\n" % (e))
bads += 1
if bads > 0:
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..bd7c27dd
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,32 @@
+[tox]
+envlist = py27,py3,pyflakes
+recreate = True
+
+[testenv]
+commands = python -m nose {posargs:tests}
+deps = -r{toxinidir}/test-requirements.txt
+ -r{toxinidir}/requirements.txt
+
+[testenv:py3]
+basepython = python3
+
+[testenv:pyflakes]
+basepython = python3
+commands = {envpython} -m pyflakes {posargs:cloudinit/ tests/ tools/}
+ {envpython} -m pep8 {posargs:cloudinit/ tests/ tools/}
+
+# https://github.com/gabrielfalcao/HTTPretty/issues/223
+setenv =
+ LC_ALL = en_US.utf-8
+
+[testenv:py26]
+commands = nosetests {posargs:tests}
+deps =
+ contextlib2
+ httpretty>=0.7.1
+ mock
+ nose
+ pep8==1.5.7
+ pyflakes
+setenv =
+ LC_ALL = C
diff --git a/udev/66-azure-ephemeral.rules b/udev/66-azure-ephemeral.rules
new file mode 100644
index 00000000..b9c5c3ef
--- /dev/null
+++ b/udev/66-azure-ephemeral.rules
@@ -0,0 +1,18 @@
+# Azure specific rules
+ACTION!="add|change", GOTO="cloud_init_end"
+SUBSYSTEM!="block", GOTO="cloud_init_end"
+ATTRS{ID_VENDOR}!="Msft", GOTO="cloud_init_end"
+ATTRS{ID_MODEL}!="Virtual_Disk", GOTO="cloud_init_end"
+
+# Root has a GUID of 0000 as the second value
+# The resource/resource has GUID of 0001 as the second value
+ATTRS{device_id}=="?00000000-0000-*", ENV{fabric_name}="azure_root", GOTO="ci_azure_names"
+ATTRS{device_id}=="?00000000-0001-*", ENV{fabric_name}="azure_resource", GOTO="ci_azure_names"
+GOTO="cloud_init_end"
+
+# Create the symlinks
+LABEL="ci_azure_names"
+ENV{DEVTYPE}=="disk", SYMLINK+="disk/cloud/$env{fabric_name}"
+ENV{DEVTYPE}=="partition", SYMLINK+="disk/cloud/$env{fabric_name}-part%n"
+
+LABEL="cloud_init_end"
diff --git a/udev/79-cloud-init-net-wait.rules b/udev/79-cloud-init-net-wait.rules
new file mode 100644
index 00000000..8344222a
--- /dev/null
+++ b/udev/79-cloud-init-net-wait.rules
@@ -0,0 +1,10 @@
+# cloud-init cold/hot-plug blocking mechanism
+# this file blocks further processing of network events
+# until cloud-init local has had a chance to read and apply network
+SUBSYSTEM!="net", GOTO="cloudinit_naming_end"
+ACTION!="add", GOTO="cloudinit_naming_end"
+
+IMPORT{program}="/lib/udev/cloud-init-wait"
+
+LABEL="cloudinit_naming_end"
+# vi: ts=4 expandtab syntax=udevrules
diff --git a/udev/cloud-init-wait b/udev/cloud-init-wait
new file mode 100755
index 00000000..b434005d
--- /dev/null
+++ b/udev/cloud-init-wait
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+CI_NET_READY="/run/cloud-init/network-config-ready"
+LOG="/run/cloud-init/${0##*/}.log"
+LOG_INIT=0
+MAX_WAIT=60
+DEBUG=0
+
+block_until_ready() {
+ local fname="$1" max="$2"
+ [ -f "$fname" ] && return 0
+ # udevadm settle below will exit at the first of 3 conditions
+ # 1.) timeout 2.) file exists 3.) all in-flight udev events are processed
+ # since this is being run from a udev event, the 3 wont happen.
+ # thus, this is essentially a inotify wait or timeout on a file in /run
+ # that is created by cloud-init-local.
+ udevadm settle "--timeout=$max" "--exit-if-exists=$fname"
+}
+
+log() {
+ [ -n "${LOG}" ] || return
+ [ "${DEBUG:-0}" = "0" ] && return
+
+ if [ $LOG_INIT = 0 ]; then
+ if [ -d "${LOG%/*}" ] || mkdir -p "${LOG%/*}"; then
+ LOG_INIT=1
+ else
+ echo "${0##*/}: WARN: log init to ${LOG%/*}" 1>&2
+ return
+ fi
+ elif [ "$LOG_INIT" = "-1" ]; then
+ return
+ fi
+ local info="$$ $INTERFACE"
+ if [ "$DEBUG" -gt 1 ]; then
+ local up idle
+ read up idle < /proc/uptime
+ info="$$ $INTERFACE $up"
+ fi
+ echo "[$info]" "$@" >> "$LOG"
+}
+
+main() {
+ local name="" readyfile="$CI_NET_READY"
+ local info="INTERFACE=${INTERFACE} ID_NET_NAME=${ID_NET_NAME}"
+ info="$info ID_NET_NAME_PATH=${ID_NET_NAME_PATH}"
+ info="$info MAC_ADDRESS=${MAC_ADDRESS}"
+ log "$info"
+
+ ## Check to see if cloud-init.target is set. If cloud-init is
+ ## disabled we do not want to do anything.
+ if [ ! -f "/run/cloud-init/enabled" ]; then
+ log "cloud-init disabled"
+ return 0
+ fi
+
+ if [ "${INTERFACE#lo}" != "$INTERFACE" ]; then
+ return 0
+ fi
+
+ block_until_ready "$readyfile" "$MAX_WAIT" ||
+ { log "failed waiting for ready on $INTERFACE"; return 1; }
+
+ log "net config ready"
+}
+
+main "$@"
+exit
+
+# vi: ts=4 expandtab