[libvirt] [PATCH 00/10] qemu: Fixes of the blockdev job handling (a blockdev-add series)
by Peter Krempa
While playing with the incremental backup stuff I came across few corner
cases in the blockdev job handling.
Peter Krempa (10):
qemu: monitor: Finish implementation of infrastructure for
'query-jobs'
qemu: blockjob: Properly propagate cancellation of blockjobs
qemu: process: Move block job refresh after async job recovery
qemu: blockjob: Fix deadlock when terminating job with invalid data
qemu: blockjob: Log blockjobs which are dropped when untracked by qemu
qemu: blockjob: Mark job with broken data but tracked by qemu as
reconnected
qemu: blockjob: Don't stop processing the finished job early
qemu: blockjob: Separate clearing of per-job data
qemu: blockjob: Introduce "broken" block job type
qemu: blockjob: Finish handling job with broken data
src/qemu/qemu_blockjob.c | 70 ++++++++++++-------
src/qemu/qemu_blockjob.h | 3 +
src/qemu/qemu_domain.c | 13 +++-
src/qemu/qemu_driver.c | 1 +
src/qemu/qemu_monitor.h | 4 +-
src/qemu/qemu_monitor_json.c | 6 ++
src/qemu/qemu_process.c | 6 +-
.../blockjob-blockdev-in.xml | 1 +
8 files changed, 73 insertions(+), 31 deletions(-)
--
2.23.0
4 years, 12 months
[libvirt] [PATCH 0/9] domain job stats handling improvements (incremental backup prequels)
by Peter Krempa
Improve few aspecs of the domain job stats handling. I felt these make
life simpler when using the domain stats specifically when trying to
figure out how stuff works.
Peter Krempa (9):
virsh: domain: Extract the code converting domain job stats to
virDomainJobInfo
api: Allow keeping completed domain job stats when reading them
virsh: Implement VIR_DOMAIN_JOB_STATS_COMPLETED_KEEP for 'domjobinfo'
qemu: Implement VIR_DOMAIN_JOB_STATS_COMPLETED_KEEP
virsh: domjobinfo: Print also job operation for failed jobs
virsh: domjobinfo: Allow printing stats also for failed and other jobs
virsh: domjobinfo: Add switch to print raw fields
API: Introduce VIR_DOMAIN_JOB_SUCCESS field for virDomainGetJobStats
qemu: Always reset @info in qemuDomainGetJobInfo
include/libvirt/libvirt-domain.h | 9 +++
src/libvirt-domain.c | 6 +-
src/qemu/qemu_driver.c | 9 ++-
tools/virsh-domain.c | 124 +++++++++++++++++++++----------
tools/virsh.pod | 16 +++-
5 files changed, 115 insertions(+), 49 deletions(-)
--
2.23.0
4 years, 12 months
[libvirt] [PATCH] util: whitelist Oracle ACFS as a shared filesystem
by Daniel P. Berrangé
The magic number is taken from the coreutils stat.c file since
there is no constant for it in normal system headers.
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/util/virfile.c | 8 +++++++-
src/util/virfile.h | 1 +
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/util/virfile.c b/src/util/virfile.c
index fca7ff9d35..3dd2b1a527 100644
--- a/src/util/virfile.c
+++ b/src/util/virfile.c
@@ -3426,6 +3426,8 @@ int virFilePrintf(FILE *fp, const char *msg, ...)
# define QB_MAGIC 0x51626d6e
# endif
+# define VIR_ACFS_MAGIC 0x61636673
+
# define PROC_MOUNTS "/proc/mounts"
static int
@@ -3578,6 +3580,9 @@ virFileIsSharedFSType(const char *path,
if ((fstypes & VIR_FILE_SHFS_QB) &&
(f_type == QB_MAGIC))
return 1;
+ if ((fstypes & VIR_FILE_SHFS_ACFS) &&
+ (f_type == VIR_ACFS_MAGIC))
+ return 1;
return 0;
}
@@ -3765,7 +3770,8 @@ int virFileIsSharedFS(const char *path)
VIR_FILE_SHFS_CIFS |
VIR_FILE_SHFS_CEPH |
VIR_FILE_SHFS_GPFS|
- VIR_FILE_SHFS_QB);
+ VIR_FILE_SHFS_QB |
+ VIR_FILE_SHFS_ACFS);
}
diff --git a/src/util/virfile.h b/src/util/virfile.h
index 9a8709b52c..bcae40ee06 100644
--- a/src/util/virfile.h
+++ b/src/util/virfile.h
@@ -209,6 +209,7 @@ enum {
VIR_FILE_SHFS_CEPH = (1 << 6),
VIR_FILE_SHFS_GPFS = (1 << 7),
VIR_FILE_SHFS_QB = (1 << 8),
+ VIR_FILE_SHFS_ACFS = (1 << 9),
};
int virFileIsSharedFSType(const char *path, int fstypes) ATTRIBUTE_NONNULL(1);
--
2.21.0
4 years, 12 months
[libvirt] [PATCH] libxl: fix typo in error message
by Daniel P. Berrangé
Signed-off-by: Daniel P. Berrangé <berrange(a)redhat.com>
---
src/libxl/libxl_conf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
index e6c6f5f6ad..1e9d5dc875 100644
--- a/src/libxl/libxl_conf.c
+++ b/src/libxl/libxl_conf.c
@@ -2384,7 +2384,7 @@ libxlDriverNodeGetInfo(libxlDriverPrivatePtr driver, virNodeInfoPtr info)
if (virStrcpyStatic(info->model, virArchToString(hostarch)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
- _("machine type %s too big for destination"),
+ _("host arch %s too big for destination"),
virArchToString(hostarch));
goto cleanup;
}
--
2.23.0
4 years, 12 months
[libvirt] [PATCH] all: don't wait for driver lock during startup
by Michal Privoznik
There are two daemons that wait for acquiring their pid files:
virtnetworkd and virtstoraged. This is undesirable as the idea
is to quit early if unable to acquire the pid file. This was
missed in v5.6.0-rc1~207.
Signed-off-by: Michal Privoznik <mprivozn(a)redhat.com>
---
src/network/bridge_driver.c | 2 +-
src/storage/storage_driver.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 07dba8cfe4..e360645969 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -761,7 +761,7 @@ networkStateInitialize(bool privileged,
if ((network_driver->lockFD =
virPidFileAcquire(network_driver->stateDir, "driver",
- true, getpid())) < 0)
+ false, getpid())) < 0)
goto error;
/* if this fails now, it will be retried later with dnsmasqCapsRefresh() */
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index d8355d3c3c..580a5e6f15 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -296,7 +296,7 @@ storageStateInitialize(bool privileged,
if ((driver->lockFD =
virPidFileAcquire(driver->stateDir, "driver",
- true, getpid())) < 0)
+ false, getpid())) < 0)
goto error;
if (virStoragePoolObjLoadAllState(driver->pools,
--
2.23.0
4 years, 12 months
[libvirt] [PATCH 0/2] driver: change scope of virDomainGetHostname to
by jcfaracco@gmail.com
From: Julio Faracco <jcfaracco(a)gmail.com>
The first patch add a new parameter into virDomainGetHostname to specify
which source an user would like to fetch hostname. QEMU supports agent
and lease from dnsmasq. Drivers that are not supporting any of these,
are included into default section of switch-case structure. So, they are
not affected by missing source argument.
The second patch, introduce this missing command for LXC. LXC could
retrieve hostname from 'lxc.uts.name' setting. But an user can change it
during container execution. It could be get from lease information.
Julio Faracco (2):
driver: Include source parameter to virDomainGetHostname
lxc: Introduce lxcDomainGetHostname
include/libvirt/libvirt-domain.h | 10 +++++
src/driver-hypervisor.h | 1 +
src/libvirt-domain.c | 7 +++-
src/lxc/lxc_driver.c | 71 ++++++++++++++++++++++++++++++++
src/openvz/openvz_driver.c | 30 ++++++++++----
src/qemu/qemu_driver.c | 67 +++++++++++++++++++++++++-----
src/remote/remote_protocol.x | 1 +
src/remote_protocol-structs | 1 +
src/test/test_driver.c | 1 +
tools/virsh-domain.c | 25 ++++++++++-
10 files changed, 191 insertions(+), 23 deletions(-)
--
2.20.1
4 years, 12 months
[libvirt] [jenkins-ci] guests: Add support for openSUSE Leap 15.1
by Jim Fehlig
Signed-off-by: Jim Fehlig <jfehlig(a)suse.com>
---
guests/configs/autoinst.xml | 86 +++++++++++++++++++
.../libvirt-opensuse-15.1/docker.yml | 2 +
.../libvirt-opensuse-15.1/install.yml | 2 +
.../host_vars/libvirt-opensuse-15.1/main.yml | 22 +++++
guests/inventory | 1 +
guests/lcitool | 2 +
guests/vars/mappings.yml | 38 +++++++-
7 files changed, 151 insertions(+), 2 deletions(-)
diff --git a/guests/configs/autoinst.xml b/guests/configs/autoinst.xml
new file mode 100644
index 0000000..9b93a29
--- /dev/null
+++ b/guests/configs/autoinst.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE profile>
+<profile
+ xmlns="http://www.suse.com/1.0/yast2ns"
+ xmlns:config="http://www.suse.com/1.0/configns">
+ <general>
+ <mode>
+ <confirm config:type="boolean">false</confirm>
+ </mode>
+ </general>
+ <partitioning config:type="list">
+ <drive>
+ <device>/dev/vda</device>
+ <use>all</use>
+ <partitions config:type="list">
+ <partition>
+ <filesystem config:type="symbol">swap</filesystem>
+ <size>500M</size>
+ <mount>swap</mount>
+ </partition>
+ <partition>
+ <filesystem config:type="symbol">ext4</filesystem>
+ <mount>/</mount>
+ <size>max</size>
+ </partition>
+ </partitions>
+ </drive>
+ </partitioning>
+ <timezone>
+ <hwclock>UTC</hwclock>
+ <timezone>UTC</timezone>
+ </timezone>
+ <add-on>
+ <add_on_products config:type="list">
+ <listentry>
+ <media_url>http://download.opensuse.org/distribution/leap/15.1/repo/oss/</media_url>
+ <name>repo-oss</name>
+ </listentry>
+ <listentry>
+ <media_url>http://download.opensuse.org/update/leap/15.1/oss</media_url>
+ <name>repo-update</name>
+ </listentry>
+ <listentry>
+ <media_url>http://download.opensuse.org/distribution/leap/15.1/repo/non-oss/</media_url>
+ <name>repo-non-oss</name>
+ </listentry>
+ <listentry>
+ <media_url>http://download.opensuse.org/update/leap/15.1/non-oss/</media_url>
+ <name>repo-update-non-oss</name>
+ </listentry>
+ </add_on_products>
+ </add-on>
+ <software>
+ <install_recommended config:type="boolean">false</install_recommended>
+ <patterns config:type="list">
+ <pattern>base</pattern>
+ <pattern>minimal_base</pattern>
+ <pattern>yast2_basis</pattern>
+ </patterns>
+ </software>
+ <networking>
+ <keep_install_network config:type="boolean">true</keep_install_network>
+ </networking>
+ <users config:type="list">
+ <user>
+ <username>root</username>
+ <user_password>root</user_password>
+ <encrypted config:type="boolean">false</encrypted>
+ <uid>0</uid>
+ <gid>0</gid>
+ <home>/root</home>
+ <shell>/bin/bash</shell>
+ </user>
+ </users>
+ <services-manager>
+ <default_target>multi-user</default_target>
+ <services>
+ <enable config:type="list">
+ <service>sshd</service>
+ </enable>
+ </services>
+ </services-manager>
+ <firewall>
+ <enable_firewall>true</enable_firewall>
+ </firewall>
+</profile>
diff --git a/guests/host_vars/libvirt-opensuse-15.1/docker.yml b/guests/host_vars/libvirt-opensuse-15.1/docker.yml
new file mode 100644
index 0000000..1fcbc38
--- /dev/null
+++ b/guests/host_vars/libvirt-opensuse-15.1/docker.yml
@@ -0,0 +1,2 @@
+---
+docker_base: openSUSE:15.1
diff --git a/guests/host_vars/libvirt-opensuse-15.1/install.yml b/guests/host_vars/libvirt-opensuse-15.1/install.yml
new file mode 100644
index 0000000..d0fdbe5
--- /dev/null
+++ b/guests/host_vars/libvirt-opensuse-15.1/install.yml
@@ -0,0 +1,2 @@
+---
+install_url: http://download.opensuse.org/distribution/leap/15.1/repo/oss/
diff --git a/guests/host_vars/libvirt-opensuse-15.1/main.yml b/guests/host_vars/libvirt-opensuse-15.1/main.yml
new file mode 100644
index 0000000..cdd4509
--- /dev/null
+++ b/guests/host_vars/libvirt-opensuse-15.1/main.yml
@@ -0,0 +1,22 @@
+---
+projects:
+ - libosinfo
+ - libvirt
+ - libvirt-cim
+ - libvirt-dbus
+ - libvirt-glib
+ - libvirt-perl
+ - libvirt-python
+ - libvirt-sandbox
+ - libvirt-tck
+ - osinfo-db
+ - osinfo-db-tools
+ - virt-manager
+ - virt-viewer
+
+package_format: 'rpm'
+package_manager: 'zypper'
+os_name: 'openSUSE'
+os_version: '15.1'
+
+ansible_python_interpreter: /usr/bin/python3
diff --git a/guests/inventory b/guests/inventory
index 3b15513..c86f8cc 100644
--- a/guests/inventory
+++ b/guests/inventory
@@ -8,5 +8,6 @@ libvirt-fedora-rawhide
libvirt-freebsd-11
libvirt-freebsd-12
libvirt-freebsd-current
+libvirt-opensuse-15.1
libvirt-ubuntu-16
libvirt-ubuntu-18
diff --git a/guests/lcitool b/guests/lcitool
index a630971..39e6853 100755
--- a/guests/lcitool
+++ b/guests/lcitool
@@ -531,6 +531,8 @@ class Application:
install_config = "preseed.cfg"
elif facts["os_name"] in ["CentOS", "Fedora"]:
install_config = "kickstart.cfg"
+ elif facts["os_name"] == "openSUSE":
+ install_config = "autoinst.xml"
else:
raise Exception(
"Host {} doesn't support installation".format(host)
diff --git a/guests/vars/mappings.yml b/guests/vars/mappings.yml
index bd9b161..ca8eb84 100644
--- a/guests/vars/mappings.yml
+++ b/guests/vars/mappings.yml
@@ -19,10 +19,10 @@
# - deb, pkg, rpm
#
# Valid OS names are:
-# - CentOS, Debian, Fedora, FreeBSD, Ubuntu
+# - CentOS, Debian, Fedora, FreeBSD, openSUSE, Ubuntu
#
# Valid OS versions are:
-# - CentOS7, Debian9, FedoraRawhide, Ubuntu18 and so on
+# - CentOS7, Debian9, FedoraRawhide, openSUSE15.1, Ubuntu18 and so on
#
# The arch specific rules use a prefix "$ARCH-" where $ARCH
# is a libvirt arch name.
@@ -70,6 +70,7 @@ mappings:
apparmor:
deb: libapparmor-dev
+ openSUSE: libapparmor-devel
cross-policy-deb: foreign
augeas:
@@ -117,6 +118,7 @@ mappings:
cppi:
Fedora: cppi
FreeBSD: cppi
+ openSUSE: cppi
cyrus-sasl:
deb: libsasl2-dev
@@ -127,6 +129,7 @@ mappings:
dbus-daemon:
default: dbus
Fedora: dbus-daemon
+ openSUSE: dbus-1
device-mapper:
deb: libdevmapper-dev
@@ -196,6 +199,7 @@ mappings:
deb: libglusterfs-dev
rpm: glusterfs-api-devel
Debian9: glusterfs-common
+ openSUSE: glusterfs-devel
Ubuntu16: glusterfs-common
Ubuntu18: glusterfs-common
cross-policy-deb: foreign
@@ -204,6 +208,7 @@ mappings:
deb: libgnutls28-dev
pkg: gnutls
rpm: gnutls-devel
+ openSUSE: libgnutls-devel
cross-policy-deb: foreign
go:
@@ -228,6 +233,7 @@ mappings:
gtk-update-icon-cache:
default: gtk-update-icon-cache
+ openSUSE: gtk3-tools
Ubuntu16: libgtk2.0-bin
gtk-vnc2:
@@ -248,14 +254,17 @@ mappings:
ip:
deb: iproute2
rpm: iproute
+ openSUSE: iproute2
iscsiadm:
deb: open-iscsi
rpm: iscsi-initiator-utils
+ openSUSE: open-iscsi
isoinfo:
default: genisoimage
FreeBSD: cdrkit
+ openSUSE: mkisofs
java:
deb: openjdk-11-jre-headless
@@ -289,6 +298,7 @@ mappings:
libaudit:
deb: libaudit-dev
rpm: audit-libs-devel
+ openSUSE: audit-devel
cross-policy-deb: foreign
libblkid:
@@ -320,6 +330,7 @@ mappings:
deb: libdbus-1-dev
pkg: dbus
rpm: dbus-devel
+ openSUSE: dbus-1-devel
cross-policy-deb: foreign
libgovirt:
@@ -347,6 +358,7 @@ mappings:
rpm: numactl-devel
armv6l-deb:
armv7l-deb:
+ openSUSE: libnuma-devel
cross-policy-deb: foreign
libparted:
@@ -370,6 +382,7 @@ mappings:
deb: librbd-dev
Fedora: librbd-devel
CentOS7: librbd1-devel
+ openSUSE: librbd-devel
cross-policy-deb: foreign
libselinux:
@@ -436,6 +449,7 @@ mappings:
deb: locales
Fedora: glibc-langpack-en
FreeBSD:
+ openSUSE: glibc-locale
lsof:
default: lsof
@@ -458,6 +472,7 @@ mappings:
ninja:
default: ninja-build
FreeBSD: ninja
+ openSUSE: ninja
mingw32-curl:
Fedora: mingw32-curl
@@ -639,6 +654,7 @@ mappings:
netcf:
deb: libnetcf-dev
rpm: netcf-devel
+ openSUSE:
cross-policy-deb: skip
net-tools:
@@ -678,6 +694,7 @@ mappings:
deb: perl
pkg: perl5
rpm: perl-Archive-Tar
+ openSUSE: perl-Archive-Tar-Wrapper
perl-CPAN-Changes:
deb: libcpan-changes-perl
@@ -709,6 +726,7 @@ mappings:
deb: libio-compress-perl
pkg: p5-IO-Compress
rpm: perl-IO-Compress-Bzip2
+ openSUSE: perl-Compress-Bzip2
perl-IO-String:
deb: libio-string-perl
@@ -747,6 +765,7 @@ mappings:
deb: libtest-lwp-useragent-perl
pkg: p5-Test-LWP-UserAgent
Fedora: perl-Test-LWP-UserAgent
+ openSUSE: perl-Test-LWP-UserAgent
perl-Test-Pod:
deb: libtest-pod-perl
@@ -809,20 +828,24 @@ mappings:
deb: python-dev
pkg: python2
rpm: python2-devel
+ openSUSE: python-devel
cross-policy-deb: foreign
python2-lxml:
default: python-lxml
Fedora: python2-lxml
FreeBSD: py27-lxml
+ openSUSE: python2-lxml
python2-nose:
default: python-nose
Fedora: python2-nose
FreeBSD: py27-nose
+ openSUSE: python2-nose
python2-setuptools:
CentOS7: python2-setuptools
+ openSUSE: python2-setuptools
python3:
default: python3
@@ -831,6 +854,7 @@ mappings:
default: python3-dbus
FreeBSD: py36-dbus
CentOS7: python36-dbus
+ openSUSE: python3-dbus-python
python3-devel:
deb: python3-dev
@@ -838,17 +862,20 @@ mappings:
Fedora: python3-devel
CentOS7: python36-devel
cross-policy-deb: foreign
+ openSUSE: python3-devel
python3-gi:
deb: python3-gi
pkg: py36-gobject3
rpm: python3-gobject
CentOS7: python36-gobject
+ openSUSE: python3-gobject
python3-libxml2:
default: python3-libxml2
FreeBSD: py36-libxml2
CentOS7:
+ openSUSE: python3-libxml2-python
Ubuntu16:
python3-lxml:
@@ -864,6 +891,7 @@ mappings:
python3-pip:
CentOS7: python3-pip
Debian9: python3-pip
+ openSUSE: python3-pip
Ubuntu16: python3-pip
Ubuntu18: python3-pip
@@ -929,6 +957,7 @@ mappings:
deb: libspice-client-gtk-3.0-dev
pkg: spice-gtk
rpm: spice-gtk3-devel
+ openSUSE: spice-gtk-devel
cross-policy-deb: foreign
strace:
@@ -942,6 +971,7 @@ mappings:
deb: iproute2
rpm: iproute-tc
CentOS7: iproute
+ openSUSE: iproute2
unzip:
default: unzip
@@ -964,10 +994,12 @@ mappings:
wireshark:
deb: wireshark-dev
Fedora: wireshark-devel
+ openSUSE: wireshark-devel
cross-policy-deb: skip
xen:
Fedora: xen-devel
+ openSUSE: xen-devel
x86_64-deb: libxen-dev
armv7l-deb: libxen-dev
aarch64-deb: libxen-dev
@@ -994,6 +1026,7 @@ mappings:
xz-static:
deb: liblzma-dev
Fedora: xz-static
+ openSUSE: xz-static-devel
cross-policy-deb: foreign
yajl:
@@ -1015,6 +1048,7 @@ mappings:
zlib-static:
deb: zlib1g-dev
rpm: zlib-static
+ openSUSE: zlib-devel-static
cross-policy-deb: foreign
--
2.23.0
4 years, 12 months
[libvirt] [PATCH 0/4] qemu: Use host-model CPU on s390 by default
by Jiri Denemark
On s390 machines host-passthrough and host-model CPUs result in the same
guest ABI (with QEMU new enough to be able to tell us what "host" CPU is
expanded to, which was implemented around 2.9.0). So instead of using
host-passthrough CPU when there's no CPU specified in a domain XML we
can safely use host-model and benefit from CPU compatibility checks
during migration, snapshot restore and similar operations.
This series applies on top of "qemu: Store default CPU in domain XML"
which is already acked, but it's waiting for a QEMU patch to be applied
to 4.2.0.
You can fetch both series at once using
git fetch https://gitlab.com/jirkade/libvirt cpu-default-type
Jiri Denemark (4):
cpu_conf: Fix default value for CPU match attribute
cpu_conf: Don't format empty model for host-model CPUs
cpu_s390: Don't check match attribute for host-model CPUs
qemu: Use host-model CPU on s390 by default
src/conf/cpu_conf.c | 25 ++++----------
src/conf/cpu_conf.h | 2 +-
src/cpu/cpu_s390.c | 18 +++++-----
src/qemu/qemu_domain.c | 33 ++++++++++++-------
.../ppc64-host+guest-compat-none.xml | 4 +--
...t-cpu-kvm-ccw-virtio-4.2.s390x-latest.args | 4 ++-
.../cpu-check-default-partial.xml | 4 +--
.../cpu-host-model-features.xml | 1 -
...lt-cpu-kvm-ccw-virtio-4.2.s390x-latest.xml | 2 +-
9 files changed, 45 insertions(+), 48 deletions(-)
--
2.24.0
4 years, 12 months
[libvirt] [PATCH 0/2] Fix a VM startup failure: QOS must be defined for network 'default'
by Erik Skultety
Caused by patching a coverity false positive in commit
f4db846c32c0a1e99a0f62b340273e48f8a98ed3.
Erik Skultety (2):
Revert "network: Check for QOS before blindly using it"
util: virNetDevBandwidthPlug: Drop ATTRIBUTE_UNUSED(4)
src/network/bridge_driver.c | 14 --------------
src/util/virnetdevbandwidth.h | 3 +--
2 files changed, 1 insertion(+), 16 deletions(-)
--
2.23.0
4 years, 12 months